riched20/tests: Test EM_GETSELTEXT with multibyte character.
[wine.git] / dlls / riched20 / tests / editor.c
blobbfd9ba5da43bed528c3edea5d6c8008188ce3789
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;
49 static BOOL is_lang_japanese;
51 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
52 HWND hwnd;
53 hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
54 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
55 hmoduleRichEdit, NULL);
56 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
57 return hwnd;
60 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
61 HWND hwnd;
62 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
63 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
64 hmoduleRichEdit, NULL);
65 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
66 return hwnd;
69 static HWND new_richedit(HWND parent) {
70 return new_window(RICHEDIT_CLASS20A, ES_MULTILINE, parent);
73 static HWND new_richeditW(HWND parent) {
74 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
77 /* Keeps the window reponsive for the deley_time in seconds.
78 * This is useful for debugging a test to see what is happening. */
79 static void keep_responsive(time_t delay_time)
81 MSG msg;
82 time_t end;
84 /* The message pump uses PeekMessage() to empty the queue and then
85 * sleeps for 50ms before retrying the queue. */
86 end = time(NULL) + delay_time;
87 while (time(NULL) < end) {
88 if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
89 TranslateMessage(&msg);
90 DispatchMessageA(&msg);
91 } else {
92 Sleep(50);
97 static void simulate_typing_characters(HWND hwnd, const char* szChars)
99 int ret;
101 while (*szChars != '\0') {
102 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
103 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
104 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
105 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
106 szChars++;
110 static BOOL hold_key(int vk)
112 BYTE key_state[256];
113 BOOL result;
115 result = GetKeyboardState(key_state);
116 ok(result, "GetKeyboardState failed.\n");
117 if (!result) return FALSE;
118 key_state[vk] |= 0x80;
119 result = SetKeyboardState(key_state);
120 ok(result, "SetKeyboardState failed.\n");
121 return result != 0;
124 static BOOL release_key(int vk)
126 BYTE key_state[256];
127 BOOL result;
129 result = GetKeyboardState(key_state);
130 ok(result, "GetKeyboardState failed.\n");
131 if (!result) return FALSE;
132 key_state[vk] &= ~0x80;
133 result = SetKeyboardState(key_state);
134 ok(result, "SetKeyboardState failed.\n");
135 return result != 0;
138 static const char haystack[] = "WINEWine wineWine wine WineWine";
139 /* ^0 ^10 ^20 ^30 */
141 struct find_s {
142 int start;
143 int end;
144 const char *needle;
145 int flags;
146 int expected_loc;
150 static struct find_s find_tests[] = {
151 /* Find in empty text */
152 {0, -1, "foo", FR_DOWN, -1},
153 {0, -1, "foo", 0, -1},
154 {0, -1, "", FR_DOWN, -1},
155 {20, 5, "foo", FR_DOWN, -1},
156 {5, 20, "foo", FR_DOWN, -1}
159 static struct find_s find_tests2[] = {
160 /* No-result find */
161 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
162 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
164 /* Subsequent finds */
165 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
166 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
167 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
168 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
170 /* Find backwards */
171 {19, 20, "Wine", FR_MATCHCASE, 13},
172 {10, 20, "Wine", FR_MATCHCASE, 4},
173 {20, 10, "Wine", FR_MATCHCASE, 13},
175 /* Case-insensitive */
176 {1, 31, "wInE", FR_DOWN, 4},
177 {1, 31, "Wine", FR_DOWN, 4},
179 /* High-to-low ranges */
180 {20, 5, "Wine", FR_DOWN, -1},
181 {2, 1, "Wine", FR_DOWN, -1},
182 {30, 29, "Wine", FR_DOWN, -1},
183 {20, 5, "Wine", 0, 13},
185 /* Find nothing */
186 {5, 10, "", FR_DOWN, -1},
187 {10, 5, "", FR_DOWN, -1},
188 {0, -1, "", FR_DOWN, -1},
189 {10, 5, "", 0, -1},
191 /* Whole-word search */
192 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
193 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
194 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
195 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
196 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
197 {11, -1, "winewine", FR_WHOLEWORD, 0},
198 {31, -1, "winewine", FR_WHOLEWORD, 23},
200 /* Bad ranges */
201 {5, 200, "XXX", FR_DOWN, -1},
202 {-20, 20, "Wine", FR_DOWN, -1},
203 {-20, 20, "Wine", FR_DOWN, -1},
204 {-15, -20, "Wine", FR_DOWN, -1},
205 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
207 /* Check the case noted in bug 4479 where matches at end aren't recognized */
208 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
209 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
210 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
211 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
212 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
214 /* The backwards case of bug 4479; bounds look right
215 * Fails because backward find is wrong */
216 {19, 20, "WINE", FR_MATCHCASE, 0},
217 {0, 20, "WINE", FR_MATCHCASE, -1},
219 {0, -1, "wineWine wine", 0, -1},
222 static WCHAR *atowstr(const char *str)
224 WCHAR *ret;
225 DWORD len;
226 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
227 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
228 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
229 return ret;
232 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
234 int findloc;
236 if(unicode){
237 FINDTEXTW ftw;
238 memset(&ftw, 0, sizeof(ftw));
239 ftw.chrg.cpMin = f->start;
240 ftw.chrg.cpMax = f->end;
241 ftw.lpstrText = atowstr(f->needle);
243 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
244 ok(findloc == f->expected_loc,
245 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
246 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
248 findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
249 ok(findloc == f->expected_loc,
250 "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
251 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
253 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
254 }else{
255 FINDTEXTA fta;
256 memset(&fta, 0, sizeof(fta));
257 fta.chrg.cpMin = f->start;
258 fta.chrg.cpMax = f->end;
259 fta.lpstrText = f->needle;
261 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
262 ok(findloc == f->expected_loc,
263 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
264 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
268 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
269 int id, BOOL unicode)
271 int findloc;
272 int expected_end_loc;
274 if(unicode){
275 FINDTEXTEXW ftw;
276 memset(&ftw, 0, sizeof(ftw));
277 ftw.chrg.cpMin = f->start;
278 ftw.chrg.cpMax = f->end;
279 ftw.lpstrText = atowstr(f->needle);
280 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
281 ok(findloc == f->expected_loc,
282 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
283 name, id, f->needle, f->start, f->end, f->flags, findloc);
284 ok(ftw.chrgText.cpMin == f->expected_loc,
285 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
286 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
287 expected_end_loc = ((f->expected_loc == -1) ? -1
288 : f->expected_loc + strlen(f->needle));
289 ok(ftw.chrgText.cpMax == expected_end_loc,
290 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
291 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
292 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
293 }else{
294 FINDTEXTEXA fta;
295 memset(&fta, 0, sizeof(fta));
296 fta.chrg.cpMin = f->start;
297 fta.chrg.cpMax = f->end;
298 fta.lpstrText = f->needle;
299 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
300 ok(findloc == f->expected_loc,
301 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
302 name, id, f->needle, f->start, f->end, f->flags, findloc);
303 ok(fta.chrgText.cpMin == f->expected_loc,
304 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
305 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
306 expected_end_loc = ((f->expected_loc == -1) ? -1
307 : f->expected_loc + strlen(f->needle));
308 ok(fta.chrgText.cpMax == expected_end_loc,
309 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
310 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
314 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
315 int num_tests, BOOL unicode)
317 int i;
319 for (i = 0; i < num_tests; i++) {
320 check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
321 check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
325 static void test_EM_FINDTEXT(BOOL unicode)
327 HWND hwndRichEdit;
328 CHARFORMAT2A cf2;
330 if(unicode)
331 hwndRichEdit = new_richeditW(NULL);
332 else
333 hwndRichEdit = new_richedit(NULL);
335 /* Empty rich edit control */
336 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
337 sizeof(find_tests)/sizeof(struct find_s), unicode);
339 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
341 /* Haystack text */
342 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
343 sizeof(find_tests2)/sizeof(struct find_s), unicode);
345 /* Setting a format on an arbitrary range should have no effect in search
346 results. This tests correct offset reporting across runs. */
347 cf2.cbSize = sizeof(CHARFORMAT2A);
348 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
349 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
350 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
351 SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
352 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
354 /* Haystack text, again */
355 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
356 sizeof(find_tests2)/sizeof(struct find_s), unicode);
358 /* Yet another range */
359 cf2.dwMask = CFM_BOLD | cf2.dwMask;
360 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
361 SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
362 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
364 /* Haystack text, again */
365 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
366 sizeof(find_tests2)/sizeof(struct find_s), unicode);
368 DestroyWindow(hwndRichEdit);
371 static const struct getline_s {
372 int line;
373 size_t buffer_len;
374 const char *text;
375 } gl[] = {
376 {0, 10, "foo bar\r"},
377 {1, 10, "\r"},
378 {2, 10, "bar\r"},
379 {3, 10, "\r"},
381 /* Buffer smaller than line length */
382 {0, 2, "foo bar\r"},
383 {0, 1, "foo bar\r"},
384 {0, 0, "foo bar\r"}
387 static void test_EM_GETLINE(void)
389 int i;
390 HWND hwndRichEdit = new_richedit(NULL);
391 static const int nBuf = 1024;
392 char dest[1024], origdest[1024];
393 const char text[] = "foo bar\n"
394 "\n"
395 "bar\n";
397 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
399 memset(origdest, 0xBB, nBuf);
400 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
402 int nCopied;
403 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
404 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
405 memset(dest, 0xBB, nBuf);
406 *(WORD *) dest = gl[i].buffer_len;
408 /* EM_GETLINE appends a "\r\0" to the end of the line
409 * nCopied counts up to and including the '\r' */
410 nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
411 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
412 expected_nCopied);
413 /* two special cases since a parameter is passed via dest */
414 if (gl[i].buffer_len == 0)
415 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
416 "buffer_len=0\n");
417 else if (gl[i].buffer_len == 1)
418 ok(dest[0] == gl[i].text[0] && !dest[1] &&
419 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
420 else
422 /* Prepare hex strings of buffers to dump on failure. */
423 char expectedbuf[1024];
424 char resultbuf[1024];
425 int j;
426 resultbuf[0] = '\0';
427 for (j = 0; j < 32; j++)
428 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
429 expectedbuf[0] = '\0';
430 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
431 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
432 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
433 sprintf(expectedbuf+strlen(expectedbuf), "??");
434 for (; j < 32; j++) /* Bytes after declared buffer size */
435 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
437 /* Test the part of the buffer that is expected to be written according
438 * to the MSDN documentation fo EM_GETLINE, which does not state that
439 * a NULL terminating character will be added unless no text is copied.
441 * Windows NT does not append a NULL terminating character, but
442 * Windows 2000 and up do append a NULL terminating character if there
443 * is space in the buffer. The test will ignore this difference. */
444 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
445 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
446 i, expected_bytes_written, expectedbuf, resultbuf);
447 /* Test the part of the buffer after the declared length to make sure
448 * there are no buffer overruns. */
449 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
450 nBuf - gl[i].buffer_len),
451 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
452 i, expected_bytes_written, expectedbuf, resultbuf);
456 DestroyWindow(hwndRichEdit);
459 static void test_EM_LINELENGTH(void)
461 HWND hwndRichEdit = new_richedit(NULL);
462 const char * text =
463 "richedit1\r"
464 "richedit1\n"
465 "richedit1\r\n"
466 "richedit1";
467 int offset_test[10][2] = {
468 {0, 9},
469 {5, 9},
470 {10, 9},
471 {15, 9},
472 {20, 9},
473 {25, 9},
474 {30, 9},
475 {35, 9},
476 {40, 0},
477 {45, 0},
479 int i;
480 LRESULT result;
482 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
484 for (i = 0; i < 10; i++) {
485 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
486 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
487 offset_test[i][0], result, offset_test[i][1]);
490 /* Test with multibyte character */
491 if (!is_lang_japanese)
492 skip("Skip multibyte character tests on non-Japanese platform\n");
493 else
495 const char *text1 =
496 "wine\n"
497 "richedit\x8e\xf0\n"
498 "wine";
499 int offset_test1[3][2] = {
500 {0, 4}, /* Line 1: |wine\n */
501 {5, 9}, /* Line 2: |richedit\x8e\xf0\n */
502 {15, 4}, /* Line 3: |wine */
504 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
505 for (i = 0; i < sizeof(offset_test1)/sizeof(offset_test1[0]); i++) {
506 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
507 ok(result == offset_test1[i][1], "Length of line at offset %d is %ld, expected %d\n",
508 offset_test1[i][0], result, offset_test1[i][1]);
512 DestroyWindow(hwndRichEdit);
515 static int get_scroll_pos_y(HWND hwnd)
517 POINT p = {-1, -1};
518 SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&p);
519 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
520 return p.y;
523 static void move_cursor(HWND hwnd, LONG charindex)
525 CHARRANGE cr;
526 cr.cpMax = charindex;
527 cr.cpMin = charindex;
528 SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
531 static void line_scroll(HWND hwnd, int amount)
533 SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
536 static void test_EM_SCROLLCARET(void)
538 int prevY, curY;
539 const char text[] = "aa\n"
540 "this is a long line of text that should be longer than the "
541 "control's width\n"
542 "cc\n"
543 "dd\n"
544 "ee\n"
545 "ff\n"
546 "gg\n"
547 "hh\n";
548 /* The richedit window height needs to be large enough vertically to fit in
549 * more than two lines of text, so the new_richedit function can't be used
550 * since a height of 60 was not large enough on some systems.
552 HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
553 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
554 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
555 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
557 /* Can't verify this */
558 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
560 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
562 /* Caret above visible window */
563 line_scroll(hwndRichEdit, 3);
564 prevY = get_scroll_pos_y(hwndRichEdit);
565 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
566 curY = get_scroll_pos_y(hwndRichEdit);
567 ok(prevY != curY, "%d == %d\n", prevY, curY);
569 /* Caret below visible window */
570 move_cursor(hwndRichEdit, sizeof(text) - 1);
571 line_scroll(hwndRichEdit, -3);
572 prevY = get_scroll_pos_y(hwndRichEdit);
573 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
574 curY = get_scroll_pos_y(hwndRichEdit);
575 ok(prevY != curY, "%d == %d\n", prevY, curY);
577 /* Caret in visible window */
578 move_cursor(hwndRichEdit, sizeof(text) - 2);
579 prevY = get_scroll_pos_y(hwndRichEdit);
580 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
581 curY = get_scroll_pos_y(hwndRichEdit);
582 ok(prevY == curY, "%d != %d\n", prevY, curY);
584 /* Caret still in visible window */
585 line_scroll(hwndRichEdit, -1);
586 prevY = get_scroll_pos_y(hwndRichEdit);
587 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
588 curY = get_scroll_pos_y(hwndRichEdit);
589 ok(prevY == curY, "%d != %d\n", prevY, curY);
591 DestroyWindow(hwndRichEdit);
594 static void test_EM_POSFROMCHAR(void)
596 HWND hwndRichEdit = new_richedit(NULL);
597 int i, expected;
598 LRESULT result;
599 unsigned int height = 0;
600 int xpos = 0;
601 POINTL pt;
602 LOCALESIGNATURE sig;
603 BOOL rtl;
604 static const char text[] = "aa\n"
605 "this is a long line of text that should be longer than the "
606 "control's width\n"
607 "cc\n"
608 "dd\n"
609 "ee\n"
610 "ff\n"
611 "gg\n"
612 "hh\n";
614 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
615 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
616 (sig.lsUsb[3] & 0x08000000) != 0);
618 /* Fill the control to lines to ensure that most of them are offscreen */
619 for (i = 0; i < 50; i++)
621 /* Do not modify the string; it is exactly 16 characters long. */
622 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
623 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
627 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
628 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
629 Richedit 3.0 accepts either of the above API conventions.
632 /* Testing Richedit 2.0 API format */
634 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
635 Since all lines are identical and drawn with the same font,
636 they should have the same height... right?
638 for (i = 0; i < 50; i++)
640 /* All the lines are 16 characters long */
641 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
642 if (i == 0)
644 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
645 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
646 xpos = LOWORD(result);
648 else if (i == 1)
650 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
651 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
652 height = HIWORD(result);
654 else
656 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
657 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 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * 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 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * 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 vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
673 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
674 for (i = 0; i < 50; i++)
676 /* All the lines are 16 characters long */
677 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
678 ok((signed short)(HIWORD(result)) == (i - 1) * height,
679 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
680 (signed short)(HIWORD(result)), (i - 1) * height);
681 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
684 /* Testing position at end of text */
685 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
686 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
687 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
689 /* Testing position way past end of text */
690 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
691 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
692 expected = (rtl ? 8 : 1);
693 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
695 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
696 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
697 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
699 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
700 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
701 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
702 xpos = LOWORD(result);
704 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
705 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
706 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
707 ok((signed short)(LOWORD(result)) < xpos,
708 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
709 (signed short)(LOWORD(result)), xpos);
710 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
712 /* Test around end of text that doesn't end in a newline. */
713 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
714 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
715 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
716 ok(pt.x > 1, "pt.x = %d\n", pt.x);
717 xpos = pt.x;
718 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
719 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
720 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
721 xpos = (rtl ? pt.x + 7 : pt.x);
722 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
723 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
724 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
726 /* Try a negative position. */
727 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
728 ok(pt.x == 1, "pt.x = %d\n", pt.x);
730 DestroyWindow(hwndRichEdit);
733 static void test_EM_SETCHARFORMAT(void)
735 HWND hwndRichEdit = new_richedit(NULL);
736 CHARFORMAT2A cf2;
737 int rc = 0;
738 int tested_effects[] = {
739 CFE_BOLD,
740 CFE_ITALIC,
741 CFE_UNDERLINE,
742 CFE_STRIKEOUT,
743 CFE_PROTECTED,
744 CFE_LINK,
745 CFE_SUBSCRIPT,
746 CFE_SUPERSCRIPT,
749 int i;
750 CHARRANGE cr;
751 LOCALESIGNATURE sig;
752 BOOL rtl;
754 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
755 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
756 (sig.lsUsb[3] & 0x08000000) != 0);
758 /* Invalid flags, CHARFORMAT2 structure blanked out */
759 memset(&cf2, 0, sizeof(cf2));
760 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
761 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
763 /* A valid flag, CHARFORMAT2 structure blanked out */
764 memset(&cf2, 0, sizeof(cf2));
765 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
766 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
768 /* A valid flag, CHARFORMAT2 structure blanked out */
769 memset(&cf2, 0, sizeof(cf2));
770 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
771 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
773 /* A valid flag, CHARFORMAT2 structure blanked out */
774 memset(&cf2, 0, sizeof(cf2));
775 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
776 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
778 /* A valid flag, CHARFORMAT2 structure blanked out */
779 memset(&cf2, 0, sizeof(cf2));
780 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
781 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
783 /* Invalid flags, CHARFORMAT2 structure minimally filled */
784 memset(&cf2, 0, sizeof(cf2));
785 cf2.cbSize = sizeof(CHARFORMAT2A);
786 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
787 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
788 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
789 ok(rc == FALSE, "Should not be able to undo here.\n");
790 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
792 /* A valid flag, CHARFORMAT2 structure minimally filled */
793 memset(&cf2, 0, sizeof(cf2));
794 cf2.cbSize = sizeof(CHARFORMAT2A);
795 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
796 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
797 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
798 ok(rc == FALSE, "Should not be able to undo here.\n");
799 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
801 /* A valid flag, CHARFORMAT2 structure minimally filled */
802 memset(&cf2, 0, sizeof(cf2));
803 cf2.cbSize = sizeof(CHARFORMAT2A);
804 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
805 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
806 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
807 ok(rc == FALSE, "Should not be able to undo here.\n");
808 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
810 /* A valid flag, CHARFORMAT2 structure minimally filled */
811 memset(&cf2, 0, sizeof(cf2));
812 cf2.cbSize = sizeof(CHARFORMAT2A);
813 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
814 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
815 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
816 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
817 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
819 /* A valid flag, CHARFORMAT2 structure minimally filled */
820 memset(&cf2, 0, sizeof(cf2));
821 cf2.cbSize = sizeof(CHARFORMAT2A);
822 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
823 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
824 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
825 ok(rc == TRUE, "Should not be able to undo here.\n");
826 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
828 cf2.cbSize = sizeof(CHARFORMAT2A);
829 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
831 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
832 cf2.cbSize = sizeof(CHARFORMAT2A);
833 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
834 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
835 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
837 /* wParam==0 is default char format, does not set modify */
838 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
839 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
840 ok(rc == 0, "Text marked as modified, expected not modified!\n");
841 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
842 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
843 if (! rtl)
845 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
846 ok(rc == 0, "Text marked as modified, expected not modified!\n");
848 else
849 skip("RTL language found\n");
851 /* wParam==SCF_SELECTION sets modify if nonempty selection */
852 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
853 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
854 ok(rc == 0, "Text marked as modified, expected not modified!\n");
855 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
856 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
857 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
858 ok(rc == 0, "Text marked as modified, expected not modified!\n");
860 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
861 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
862 ok(rc == 0, "Text marked as modified, expected not modified!\n");
863 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
864 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
865 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
866 ok(rc == 0, "Text marked as modified, expected not modified!\n");
867 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
868 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
869 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
870 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
871 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
873 /* wParam==SCF_ALL sets modify regardless of whether text is present */
874 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
875 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
876 ok(rc == 0, "Text marked as modified, expected not modified!\n");
877 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
878 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
879 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
880 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
882 DestroyWindow(hwndRichEdit);
884 /* EM_GETCHARFORMAT tests */
885 for (i = 0; tested_effects[i]; i++)
887 hwndRichEdit = new_richedit(NULL);
888 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
890 /* Need to set a TrueType font to get consistent CFM_BOLD results */
891 memset(&cf2, 0, sizeof(CHARFORMAT2A));
892 cf2.cbSize = sizeof(CHARFORMAT2A);
893 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
894 cf2.dwEffects = 0;
895 strcpy(cf2.szFaceName, "Courier New");
896 cf2.wWeight = FW_DONTCARE;
897 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
899 memset(&cf2, 0, sizeof(CHARFORMAT2A));
900 cf2.cbSize = sizeof(CHARFORMAT2A);
901 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
902 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
903 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
904 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
906 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
907 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
908 ok((cf2.dwEffects & tested_effects[i]) == 0,
909 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
911 memset(&cf2, 0, sizeof(CHARFORMAT2A));
912 cf2.cbSize = sizeof(CHARFORMAT2A);
913 cf2.dwMask = tested_effects[i];
914 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
915 cf2.dwMask = CFM_SUPERSCRIPT;
916 cf2.dwEffects = tested_effects[i];
917 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
918 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
920 memset(&cf2, 0, sizeof(CHARFORMAT2A));
921 cf2.cbSize = sizeof(CHARFORMAT2A);
922 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
923 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
924 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
925 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
927 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
928 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
929 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
930 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
932 memset(&cf2, 0, sizeof(CHARFORMAT2A));
933 cf2.cbSize = sizeof(CHARFORMAT2A);
934 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
935 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
936 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
937 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
939 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
940 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
941 ok((cf2.dwEffects & tested_effects[i]) == 0,
942 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
944 memset(&cf2, 0, sizeof(CHARFORMAT2A));
945 cf2.cbSize = sizeof(CHARFORMAT2A);
946 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
947 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
948 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
949 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
951 (cf2.dwMask & tested_effects[i]) == 0),
952 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
954 DestroyWindow(hwndRichEdit);
957 for (i = 0; tested_effects[i]; i++)
959 hwndRichEdit = new_richedit(NULL);
960 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
962 /* Need to set a TrueType font to get consistent CFM_BOLD results */
963 memset(&cf2, 0, sizeof(CHARFORMAT2A));
964 cf2.cbSize = sizeof(CHARFORMAT2A);
965 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
966 cf2.dwEffects = 0;
967 strcpy(cf2.szFaceName, "Courier New");
968 cf2.wWeight = FW_DONTCARE;
969 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
971 memset(&cf2, 0, sizeof(CHARFORMAT2A));
972 cf2.cbSize = sizeof(CHARFORMAT2A);
973 cf2.dwMask = tested_effects[i];
974 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
975 cf2.dwMask = CFM_SUPERSCRIPT;
976 cf2.dwEffects = tested_effects[i];
977 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
978 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
980 memset(&cf2, 0, sizeof(CHARFORMAT2A));
981 cf2.cbSize = sizeof(CHARFORMAT2A);
982 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
983 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
984 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
985 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
987 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
988 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
989 ok((cf2.dwEffects & tested_effects[i]) == 0,
990 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
992 memset(&cf2, 0, sizeof(CHARFORMAT2A));
993 cf2.cbSize = sizeof(CHARFORMAT2A);
994 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
995 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
996 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
997 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
999 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1000 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1001 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1002 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1004 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1005 cf2.cbSize = sizeof(CHARFORMAT2A);
1006 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1007 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1008 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1009 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1011 (cf2.dwMask & tested_effects[i]) == 0),
1012 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1013 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1014 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1016 DestroyWindow(hwndRichEdit);
1019 /* Effects applied on an empty selection should take effect when selection is
1020 replaced with text */
1021 hwndRichEdit = new_richedit(NULL);
1022 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1023 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1025 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1026 cf2.cbSize = sizeof(CHARFORMAT2A);
1027 cf2.dwMask = CFM_BOLD;
1028 cf2.dwEffects = CFE_BOLD;
1029 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1031 /* Selection is now nonempty */
1032 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1034 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1035 cf2.cbSize = sizeof(CHARFORMAT2A);
1036 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1037 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1039 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1040 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1041 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1042 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1045 /* Set two effects on an empty selection */
1046 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1047 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1049 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1050 cf2.cbSize = sizeof(CHARFORMAT2A);
1051 cf2.dwMask = CFM_BOLD;
1052 cf2.dwEffects = CFE_BOLD;
1053 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1054 cf2.dwMask = CFM_ITALIC;
1055 cf2.dwEffects = CFE_ITALIC;
1056 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1058 /* Selection is now nonempty */
1059 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1061 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1062 cf2.cbSize = sizeof(CHARFORMAT2A);
1063 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1064 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1066 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1067 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1068 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1069 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1071 /* Setting the (empty) selection to exactly the same place as before should
1072 NOT clear the insertion style! */
1073 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1074 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1076 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1077 cf2.cbSize = sizeof(CHARFORMAT2A);
1078 cf2.dwMask = CFM_BOLD;
1079 cf2.dwEffects = CFE_BOLD;
1080 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1082 /* Empty selection in same place, insert style should NOT be forgotten here. */
1083 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1085 /* Selection is now nonempty */
1086 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1088 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1089 cf2.cbSize = sizeof(CHARFORMAT2A);
1090 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1091 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1093 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1094 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1095 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1096 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1098 /* Ditto with EM_EXSETSEL */
1099 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1100 cr.cpMin = 2; cr.cpMax = 2;
1101 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1103 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1104 cf2.cbSize = sizeof(CHARFORMAT2A);
1105 cf2.dwMask = CFM_BOLD;
1106 cf2.dwEffects = CFE_BOLD;
1107 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1109 /* Empty selection in same place, insert style should NOT be forgotten here. */
1110 cr.cpMin = 2; cr.cpMax = 2;
1111 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1113 /* Selection is now nonempty */
1114 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1116 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1117 cf2.cbSize = sizeof(CHARFORMAT2A);
1118 cr.cpMin = 2; cr.cpMax = 6;
1119 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1120 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1122 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1123 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1124 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1125 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1127 DestroyWindow(hwndRichEdit);
1130 static void test_EM_SETTEXTMODE(void)
1132 HWND hwndRichEdit = new_richedit(NULL);
1133 CHARFORMAT2A cf2, cf2test;
1134 CHARRANGE cr;
1135 int rc = 0;
1137 /*Attempt to use mutually exclusive modes*/
1138 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1139 ok(rc == E_INVALIDARG,
1140 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1142 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1143 /*Insert text into the control*/
1145 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1147 /*Attempt to change the control to plain text mode*/
1148 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1149 ok(rc == E_UNEXPECTED,
1150 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1152 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1153 If rich text is pasted, it should have the same formatting as the rest
1154 of the text in the control*/
1156 /*Italicize the text
1157 *NOTE: If the default text was already italicized, the test will simply
1158 reverse; in other words, it will copy a regular "wine" into a plain
1159 text window that uses an italicized format*/
1160 cf2.cbSize = sizeof(CHARFORMAT2A);
1161 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1163 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1164 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1166 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1167 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1169 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1170 however, SCF_ALL has been implemented*/
1171 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1172 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1174 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1175 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1177 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1179 /*Select the string "wine"*/
1180 cr.cpMin = 0;
1181 cr.cpMax = 4;
1182 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1184 /*Copy the italicized "wine" to the clipboard*/
1185 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1187 /*Reset the formatting to default*/
1188 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1189 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1190 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1192 /*Clear the text in the control*/
1193 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1195 /*Switch to Plain Text Mode*/
1196 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1197 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1199 /*Input "wine" again in normal format*/
1200 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1202 /*Paste the italicized "wine" into the control*/
1203 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1205 /*Select a character from the first "wine" string*/
1206 cr.cpMin = 2;
1207 cr.cpMax = 3;
1208 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1210 /*Retrieve its formatting*/
1211 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1213 /*Select a character from the second "wine" string*/
1214 cr.cpMin = 5;
1215 cr.cpMax = 6;
1216 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1218 /*Retrieve its formatting*/
1219 cf2test.cbSize = sizeof(CHARFORMAT2A);
1220 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1222 /*Compare the two formattings*/
1223 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1224 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1225 cf2.dwEffects, cf2test.dwEffects);
1226 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1227 printing "wine" in the current format(normal)
1228 pasting "wine" from the clipboard(italicized)
1229 comparing the two formats(should differ)*/
1231 /*Attempt to switch with text in control*/
1232 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1233 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1235 /*Clear control*/
1236 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1238 /*Switch into Rich Text mode*/
1239 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1240 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1242 /*Print "wine" in normal formatting into the control*/
1243 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1245 /*Paste italicized "wine" into the control*/
1246 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1248 /*Select text from the first "wine" string*/
1249 cr.cpMin = 1;
1250 cr.cpMax = 3;
1251 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1253 /*Retrieve its formatting*/
1254 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1256 /*Select text from the second "wine" string*/
1257 cr.cpMin = 6;
1258 cr.cpMax = 7;
1259 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1261 /*Retrieve its formatting*/
1262 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1264 /*Test that the two formattings are not the same*/
1265 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1266 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1267 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1269 DestroyWindow(hwndRichEdit);
1272 static void test_SETPARAFORMAT(void)
1274 HWND hwndRichEdit = new_richedit(NULL);
1275 PARAFORMAT2 fmt;
1276 HRESULT ret;
1277 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1278 fmt.cbSize = sizeof(PARAFORMAT2);
1279 fmt.dwMask = PFM_ALIGNMENT;
1280 fmt.wAlignment = PFA_LEFT;
1282 ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1283 ok(ret != 0, "expected non-zero got %d\n", ret);
1285 fmt.cbSize = sizeof(PARAFORMAT2);
1286 fmt.dwMask = -1;
1287 ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1288 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1289 * between richedit different native builds of riched20.dll
1290 * used on different Windows versions. */
1291 ret &= ~PFM_TABLEROWDELIMITER;
1292 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1294 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1295 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1297 DestroyWindow(hwndRichEdit);
1300 static void test_TM_PLAINTEXT(void)
1302 /*Tests plain text properties*/
1304 HWND hwndRichEdit = new_richedit(NULL);
1305 CHARFORMAT2A cf2, cf2test;
1306 CHARRANGE cr;
1307 int rc = 0;
1309 /*Switch to plain text mode*/
1311 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1312 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1314 /*Fill control with text*/
1316 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1318 /*Select some text and bold it*/
1320 cr.cpMin = 10;
1321 cr.cpMax = 20;
1322 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1323 cf2.cbSize = sizeof(CHARFORMAT2A);
1324 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1326 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1327 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1329 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1330 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1332 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1333 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1335 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1336 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1338 /*Get the formatting of those characters*/
1340 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1342 /*Get the formatting of some other characters*/
1343 cf2test.cbSize = sizeof(CHARFORMAT2A);
1344 cr.cpMin = 21;
1345 cr.cpMax = 30;
1346 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1347 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1349 /*Test that they are the same as plain text allows only one formatting*/
1351 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1352 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1353 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1355 /*Fill the control with a "wine" string, which when inserted will be bold*/
1357 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1359 /*Copy the bolded "wine" string*/
1361 cr.cpMin = 0;
1362 cr.cpMax = 4;
1363 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1364 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1366 /*Swap back to rich text*/
1368 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1369 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1371 /*Set the default formatting to bold italics*/
1373 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1374 cf2.dwMask |= CFM_ITALIC;
1375 cf2.dwEffects ^= CFE_ITALIC;
1376 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1377 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1379 /*Set the text in the control to "wine", which will be bold and italicized*/
1381 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1383 /*Paste the plain text "wine" string, which should take the insert
1384 formatting, which at the moment is bold italics*/
1386 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1388 /*Select the first "wine" string and retrieve its formatting*/
1390 cr.cpMin = 1;
1391 cr.cpMax = 3;
1392 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1393 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1395 /*Select the second "wine" string and retrieve its formatting*/
1397 cr.cpMin = 5;
1398 cr.cpMax = 7;
1399 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1400 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1402 /*Compare the two formattings. They should be the same.*/
1404 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1405 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1406 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1407 DestroyWindow(hwndRichEdit);
1410 static void test_WM_GETTEXT(void)
1412 HWND hwndRichEdit = new_richedit(NULL);
1413 static const char text[] = "Hello. My name is RichEdit!";
1414 static const char text2[] = "Hello. My name is RichEdit!\r";
1415 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1416 char buffer[1024] = {0};
1417 int result;
1419 /* Baseline test with normal-sized buffer */
1420 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1421 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1422 ok(result == lstrlenA(buffer),
1423 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1424 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1425 result = strcmp(buffer,text);
1426 ok(result == 0,
1427 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1429 /* Test for returned value of WM_GETTEXTLENGTH */
1430 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1431 ok(result == lstrlenA(text),
1432 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1433 result, lstrlenA(text));
1435 /* Test for behavior in overflow case */
1436 memset(buffer, 0, 1024);
1437 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1438 ok(result == 0 ||
1439 result == lstrlenA(text) - 1, /* XP, win2k3 */
1440 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1441 result = strcmp(buffer,text);
1442 if (result)
1443 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1444 ok(result == 0,
1445 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1447 /* Baseline test with normal-sized buffer and carriage return */
1448 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1449 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1450 ok(result == lstrlenA(buffer),
1451 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1452 result = strcmp(buffer,text2_after);
1453 ok(result == 0,
1454 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1456 /* Test for returned value of WM_GETTEXTLENGTH */
1457 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1458 ok(result == lstrlenA(text2_after),
1459 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1460 result, lstrlenA(text2_after));
1462 /* Test for behavior of CRLF conversion in case of overflow */
1463 memset(buffer, 0, 1024);
1464 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1465 ok(result == 0 ||
1466 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1467 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1468 result = strcmp(buffer,text2);
1469 if (result)
1470 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1471 ok(result == 0,
1472 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1474 DestroyWindow(hwndRichEdit);
1477 static void test_EM_GETTEXTRANGE(void)
1479 HWND hwndRichEdit = new_richedit(NULL);
1480 const char * text1 = "foo bar\r\nfoo bar";
1481 const char * text2 = "foo bar\rfoo bar";
1482 const char * expect = "bar\rfoo";
1483 char buffer[1024] = {0};
1484 LRESULT result;
1485 TEXTRANGEA textRange;
1487 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1489 textRange.lpstrText = buffer;
1490 textRange.chrg.cpMin = 4;
1491 textRange.chrg.cpMax = 11;
1492 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1493 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1494 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1496 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1498 textRange.lpstrText = buffer;
1499 textRange.chrg.cpMin = 4;
1500 textRange.chrg.cpMax = 11;
1501 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1502 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1503 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1505 /* cpMax of text length is used instead of -1 in this case */
1506 textRange.lpstrText = buffer;
1507 textRange.chrg.cpMin = 0;
1508 textRange.chrg.cpMax = -1;
1509 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1510 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1511 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1513 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1514 textRange.lpstrText = buffer;
1515 textRange.chrg.cpMin = -1;
1516 textRange.chrg.cpMax = 1;
1517 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1518 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1519 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1521 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1522 textRange.lpstrText = buffer;
1523 textRange.chrg.cpMin = 1;
1524 textRange.chrg.cpMax = -1;
1525 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1526 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1527 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1529 /* no end character is copied if cpMax - cpMin < 0 */
1530 textRange.lpstrText = buffer;
1531 textRange.chrg.cpMin = 5;
1532 textRange.chrg.cpMax = 5;
1533 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1534 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1535 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1537 /* cpMax of text length is used if cpMax > text length*/
1538 textRange.lpstrText = buffer;
1539 textRange.chrg.cpMin = 0;
1540 textRange.chrg.cpMax = 1000;
1541 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1542 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1543 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1545 /* Test with multibyte character */
1546 if (!is_lang_japanese)
1547 skip("Skip multibyte character tests on non-Japanese platform\n");
1548 else
1550 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1551 textRange.chrg.cpMin = 4;
1552 textRange.chrg.cpMax = 8;
1553 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1554 todo_wine ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
1555 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1558 DestroyWindow(hwndRichEdit);
1561 static void test_EM_GETSELTEXT(void)
1563 HWND hwndRichEdit = new_richedit(NULL);
1564 const char * text1 = "foo bar\r\nfoo bar";
1565 const char * text2 = "foo bar\rfoo bar";
1566 const char * expect = "bar\rfoo";
1567 char buffer[1024] = {0};
1568 LRESULT result;
1570 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1572 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1573 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1574 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1575 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1577 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1579 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1580 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1581 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1582 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1584 /* Test with multibyte character */
1585 if (!is_lang_japanese)
1586 skip("Skip multibyte character tests on non-Japanese platform\n");
1587 else
1589 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1590 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1591 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1592 todo_wine ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
1593 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", buffer);
1596 DestroyWindow(hwndRichEdit);
1599 /* FIXME: need to test unimplemented options and robustly test wparam */
1600 static void test_EM_SETOPTIONS(void)
1602 HWND hwndRichEdit;
1603 static const char text[] = "Hello. My name is RichEdit!";
1604 char buffer[1024] = {0};
1605 DWORD dwStyle, options, oldOptions;
1606 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1607 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1608 ES_SELECTIONBAR|ES_VERTICAL;
1610 /* Test initial options. */
1611 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1612 0, 0, 200, 60, NULL, NULL,
1613 hmoduleRichEdit, NULL);
1614 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1615 RICHEDIT_CLASS20A, (int) GetLastError());
1616 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1617 ok(options == 0, "Incorrect initial options %x\n", options);
1618 DestroyWindow(hwndRichEdit);
1620 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1621 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1622 0, 0, 200, 60, NULL, NULL,
1623 hmoduleRichEdit, NULL);
1624 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1625 RICHEDIT_CLASS20A, (int) GetLastError());
1626 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1627 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1628 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1629 "Incorrect initial options %x\n", options);
1631 /* NEGATIVE TESTING - NO OPTIONS SET */
1632 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1633 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1635 /* testing no readonly by sending 'a' to the control*/
1636 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1637 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1638 ok(buffer[0]=='a',
1639 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1640 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1642 /* READONLY - sending 'a' to the control */
1643 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1644 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1645 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1646 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1647 ok(buffer[0]==text[0],
1648 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1650 /* EM_SETOPTIONS changes the window style, but changing the
1651 * window style does not change the options. */
1652 dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1653 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1654 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1655 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1656 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1657 /* Confirm that the text is still read only. */
1658 SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1659 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1660 ok(buffer[0]==text[0],
1661 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1663 oldOptions = options;
1664 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1665 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1666 ok(options == oldOptions,
1667 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1669 DestroyWindow(hwndRichEdit);
1672 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1674 CHARFORMAT2A text_format;
1675 text_format.cbSize = sizeof(text_format);
1676 SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1677 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
1678 return (text_format.dwEffects & CFE_LINK) != 0;
1681 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1683 BOOL link_present = FALSE;
1685 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1686 if (is_url)
1687 { /* control text is url; should get CFE_LINK */
1688 ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1690 else
1692 ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1696 static HWND new_static_wnd(HWND parent) {
1697 return new_window("Static", 0, parent);
1700 static void test_EM_AUTOURLDETECT(void)
1702 /* DO NOT change the properties of the first two elements. To shorten the
1703 tests, all tests after WM_SETTEXT test just the first two elements -
1704 one non-URL and one URL */
1705 struct urls_s {
1706 const char *text;
1707 BOOL is_url;
1708 } urls[12] = {
1709 {"winehq.org", FALSE},
1710 {"http://www.winehq.org", TRUE},
1711 {"http//winehq.org", FALSE},
1712 {"ww.winehq.org", FALSE},
1713 {"www.winehq.org", TRUE},
1714 {"ftp://192.168.1.1", TRUE},
1715 {"ftp//192.168.1.1", FALSE},
1716 {"mailto:your@email.com", TRUE},
1717 {"prospero:prosperoserver", TRUE},
1718 {"telnet:test", TRUE},
1719 {"news:newserver", TRUE},
1720 {"wais:waisserver", TRUE}
1723 int i, j;
1724 int urlRet=-1;
1725 HWND hwndRichEdit, parent;
1727 /* All of the following should cause the URL to be detected */
1728 const char * templates_delim[] = {
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\r on it",
1732 "This is some text with ---X--- on it",
1733 "This is some text with \"X\" on it",
1734 "This is some text with 'X' on it",
1735 "This is some text with 'X' on it",
1736 "This is some text with :X: on it",
1738 "This text ends with X",
1740 "This is some text with X) on it",
1741 "This is some text with X--- on it",
1742 "This is some text with X\" on it",
1743 "This is some text with X' on it",
1744 "This is some text with X: on it",
1746 "This is some text with (X on it",
1747 "This is some text with \rX on it",
1748 "This is some text with ---X on it",
1749 "This is some text with \"X on it",
1750 "This is some text with 'X on it",
1751 "This is some text with :X on it",
1753 /* None of these should cause the URL to be detected */
1754 const char * templates_non_delim[] = {
1755 "This is some text with |X| on it",
1756 "This is some text with *X* on it",
1757 "This is some text with /X/ on it",
1758 "This is some text with +X+ on it",
1759 "This is some text with %X% on it",
1760 "This is some text with #X# on it",
1761 "This is some text with @X@ on it",
1762 "This is some text with \\X\\ on it",
1763 "This is some text with |X on it",
1764 "This is some text with *X on it",
1765 "This is some text with /X on it",
1766 "This is some text with +X on it",
1767 "This is some text with %X on it",
1768 "This is some text with #X on it",
1769 "This is some text with @X on it",
1770 "This is some text with \\X on it",
1772 /* All of these cause the URL detection to be extended by one more byte,
1773 thus demonstrating that the tested character is considered as part
1774 of the URL. */
1775 const char * templates_xten_delim[] = {
1776 "This is some text with X| on it",
1777 "This is some text with X* on it",
1778 "This is some text with X/ on it",
1779 "This is some text with X+ on it",
1780 "This is some text with X% on it",
1781 "This is some text with X# on it",
1782 "This is some text with X@ on it",
1783 "This is some text with X\\ on it",
1785 char buffer[1024];
1787 parent = new_static_wnd(NULL);
1788 hwndRichEdit = new_richedit(parent);
1789 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1790 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1791 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1792 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1793 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1794 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1795 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1796 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1797 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1798 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1799 /* for each url, check the text to see if CFE_LINK effect is present */
1800 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1802 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1803 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1804 check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
1806 /* Link detection should happen immediately upon WM_SETTEXT */
1807 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1808 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1809 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1811 DestroyWindow(hwndRichEdit);
1813 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1814 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1815 hwndRichEdit = new_richedit(parent);
1817 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1818 char * at_pos;
1819 int at_offset;
1820 int end_offset;
1822 at_pos = strchr(templates_delim[j], 'X');
1823 at_offset = at_pos - templates_delim[j];
1824 memcpy(buffer, templates_delim[j], at_offset);
1825 buffer[at_offset] = '\0';
1826 strcat(buffer, urls[i].text);
1827 strcat(buffer, templates_delim[j] + at_offset + 1);
1828 end_offset = at_offset + strlen(urls[i].text);
1830 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1831 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1833 /* This assumes no templates start with the URL itself, and that they
1834 have at least two characters before the URL text */
1835 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1836 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1837 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1838 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1839 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1840 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1842 if (urls[i].is_url)
1844 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1845 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1846 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1847 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1849 else
1851 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1852 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1853 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1854 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1856 if (buffer[end_offset] != '\0')
1858 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1859 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1860 if (buffer[end_offset +1] != '\0')
1862 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1863 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1868 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1869 char * at_pos;
1870 int at_offset;
1871 int end_offset;
1873 at_pos = strchr(templates_non_delim[j], 'X');
1874 at_offset = at_pos - templates_non_delim[j];
1875 memcpy(buffer, templates_non_delim[j], at_offset);
1876 buffer[at_offset] = '\0';
1877 strcat(buffer, urls[i].text);
1878 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1879 end_offset = at_offset + strlen(urls[i].text);
1881 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1882 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1884 /* This assumes no templates start with the URL itself, and that they
1885 have at least two characters before the URL text */
1886 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1887 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1888 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1889 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1890 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1891 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1893 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1894 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1895 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1896 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1897 if (buffer[end_offset] != '\0')
1899 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1900 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1901 if (buffer[end_offset +1] != '\0')
1903 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1904 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1909 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1910 char * at_pos;
1911 int at_offset;
1912 int end_offset;
1914 at_pos = strchr(templates_xten_delim[j], 'X');
1915 at_offset = at_pos - templates_xten_delim[j];
1916 memcpy(buffer, templates_xten_delim[j], at_offset);
1917 buffer[at_offset] = '\0';
1918 strcat(buffer, urls[i].text);
1919 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1920 end_offset = at_offset + strlen(urls[i].text);
1922 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1923 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1925 /* This assumes no templates start with the URL itself, and that they
1926 have at least two characters before the URL text */
1927 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1928 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1929 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1930 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1931 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1932 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1934 if (urls[i].is_url)
1936 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1937 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1938 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1939 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1940 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1941 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1943 else
1945 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1946 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1947 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1948 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1949 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1950 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1952 if (buffer[end_offset +1] != '\0')
1954 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1955 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1956 if (buffer[end_offset +2] != '\0')
1958 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1959 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1964 DestroyWindow(hwndRichEdit);
1965 hwndRichEdit = NULL;
1968 /* Test detection of URLs within normal text - WM_CHAR case. */
1969 /* Test only the first two URL examples for brevity */
1970 for (i = 0; i < 2; i++) {
1971 hwndRichEdit = new_richedit(parent);
1973 /* Also for brevity, test only the first three delimiters */
1974 for (j = 0; j < 3; j++) {
1975 char * at_pos;
1976 int at_offset;
1977 int end_offset;
1978 int u, v;
1980 at_pos = strchr(templates_delim[j], 'X');
1981 at_offset = at_pos - templates_delim[j];
1982 end_offset = at_offset + strlen(urls[i].text);
1984 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1985 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
1986 for (u = 0; templates_delim[j][u]; u++) {
1987 if (templates_delim[j][u] == '\r') {
1988 simulate_typing_characters(hwndRichEdit, "\r");
1989 } else if (templates_delim[j][u] != 'X') {
1990 SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1991 } else {
1992 for (v = 0; urls[i].text[v]; v++) {
1993 SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1997 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1999 /* This assumes no templates start with the URL itself, and that they
2000 have at least two characters before the URL text */
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2003 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2004 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2005 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2006 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2008 if (urls[i].is_url)
2010 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2011 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2012 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2013 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2015 else
2017 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2018 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2019 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2020 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2022 if (buffer[end_offset] != '\0')
2024 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2025 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2026 if (buffer[end_offset +1] != '\0')
2028 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2029 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2033 /* The following will insert a paragraph break after the first character
2034 of the URL candidate, thus breaking the URL. It is expected that the
2035 CFE_LINK attribute should break across both pieces of the URL */
2036 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2037 simulate_typing_characters(hwndRichEdit, "\r");
2038 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2040 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2041 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2042 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2043 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2044 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2045 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2047 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2048 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2049 /* end_offset moved because of paragraph break */
2050 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2051 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2052 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2053 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2057 if (buffer[end_offset +2] != '\0')
2059 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2060 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2064 /* The following will remove the just-inserted paragraph break, thus
2065 restoring the URL */
2066 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2067 simulate_typing_characters(hwndRichEdit, "\b");
2068 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2070 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2071 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2072 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2073 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2074 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2075 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2077 if (urls[i].is_url)
2079 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2080 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2081 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2082 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2084 else
2086 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2087 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2088 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2089 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2091 if (buffer[end_offset] != '\0')
2093 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2094 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2095 if (buffer[end_offset +1] != '\0')
2097 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2098 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2102 DestroyWindow(hwndRichEdit);
2103 hwndRichEdit = NULL;
2106 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2107 /* Test just the first two URL examples for brevity */
2108 for (i = 0; i < 2; i++) {
2109 SETTEXTEX st;
2111 hwndRichEdit = new_richedit(parent);
2113 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2114 be detected:
2115 1) Set entire text, a la WM_SETTEXT
2116 2) Set a selection of the text to the URL
2117 3) Set a portion of the text at a time, which eventually results in
2118 an URL
2119 All of them should give equivalent results
2122 /* Set entire text in one go, like WM_SETTEXT */
2123 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2124 char * at_pos;
2125 int at_offset;
2126 int end_offset;
2128 st.codepage = CP_ACP;
2129 st.flags = ST_DEFAULT;
2131 at_pos = strchr(templates_delim[j], 'X');
2132 at_offset = at_pos - templates_delim[j];
2133 memcpy(buffer, templates_delim[j], at_offset);
2134 buffer[at_offset] = '\0';
2135 strcat(buffer, urls[i].text);
2136 strcat(buffer, templates_delim[j] + at_offset + 1);
2137 end_offset = at_offset + strlen(urls[i].text);
2139 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2140 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2142 /* This assumes no templates start with the URL itself, and that they
2143 have at least two characters before the URL text */
2144 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2145 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2146 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2147 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2148 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2149 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2151 if (urls[i].is_url)
2153 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2154 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2155 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2156 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2158 else
2160 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2161 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2162 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2163 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2165 if (buffer[end_offset] != '\0')
2167 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2168 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2169 if (buffer[end_offset +1] != '\0')
2171 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2172 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2177 /* Set selection with X to the URL */
2178 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2179 char * at_pos;
2180 int at_offset;
2181 int end_offset;
2183 at_pos = strchr(templates_delim[j], 'X');
2184 at_offset = at_pos - templates_delim[j];
2185 end_offset = at_offset + strlen(urls[i].text);
2187 st.codepage = CP_ACP;
2188 st.flags = ST_DEFAULT;
2189 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2190 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2191 st.flags = ST_SELECTION;
2192 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2193 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2194 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2196 /* This assumes no templates start with the URL itself, and that they
2197 have at least two characters before the URL text */
2198 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2199 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2200 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2201 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2202 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2203 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2205 if (urls[i].is_url)
2207 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2208 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2209 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2210 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2212 else
2214 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2215 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2216 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2217 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2219 if (buffer[end_offset] != '\0')
2221 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2222 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2223 if (buffer[end_offset +1] != '\0')
2225 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2226 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2231 /* Set selection with X to the first character of the URL, then the rest */
2232 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2233 char * at_pos;
2234 int at_offset;
2235 int end_offset;
2237 at_pos = strchr(templates_delim[j], 'X');
2238 at_offset = at_pos - templates_delim[j];
2239 end_offset = at_offset + strlen(urls[i].text);
2241 strcpy(buffer, "YY");
2242 buffer[0] = urls[i].text[0];
2244 st.codepage = CP_ACP;
2245 st.flags = ST_DEFAULT;
2246 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2247 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2248 st.flags = ST_SELECTION;
2249 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2250 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2251 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2252 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2253 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2255 /* This assumes no templates start with the URL itself, and that they
2256 have at least two characters before the URL text */
2257 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2258 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2259 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2260 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2261 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2262 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2264 if (urls[i].is_url)
2266 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2267 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2268 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2269 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2271 else
2273 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2274 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2275 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2276 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2278 if (buffer[end_offset] != '\0')
2280 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2281 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2282 if (buffer[end_offset +1] != '\0')
2284 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2285 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2290 DestroyWindow(hwndRichEdit);
2291 hwndRichEdit = NULL;
2294 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2295 /* Test just the first two URL examples for brevity */
2296 for (i = 0; i < 2; i++) {
2297 hwndRichEdit = new_richedit(parent);
2299 /* Set selection with X to the URL */
2300 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2301 char * at_pos;
2302 int at_offset;
2303 int end_offset;
2305 at_pos = strchr(templates_delim[j], 'X');
2306 at_offset = at_pos - templates_delim[j];
2307 end_offset = at_offset + strlen(urls[i].text);
2309 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2310 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2311 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2312 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2313 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2315 /* This assumes no templates start with the URL itself, and that they
2316 have at least two characters before the URL text */
2317 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2318 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2319 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2320 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2321 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2322 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2324 if (urls[i].is_url)
2326 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2327 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2328 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2329 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2331 else
2333 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2334 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2335 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2336 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2338 if (buffer[end_offset] != '\0')
2340 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2341 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2342 if (buffer[end_offset +1] != '\0')
2344 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2345 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2350 /* Set selection with X to the first character of the URL, then the rest */
2351 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2352 char * at_pos;
2353 int at_offset;
2354 int end_offset;
2356 at_pos = strchr(templates_delim[j], 'X');
2357 at_offset = at_pos - templates_delim[j];
2358 end_offset = at_offset + strlen(urls[i].text);
2360 strcpy(buffer, "YY");
2361 buffer[0] = urls[i].text[0];
2363 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2364 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2365 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2366 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2367 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2368 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2369 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2371 /* This assumes no templates start with the URL itself, and that they
2372 have at least two characters before the URL text */
2373 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2374 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2375 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2376 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2377 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2378 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2380 if (urls[i].is_url)
2382 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2383 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2384 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2385 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2387 else
2389 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2390 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2391 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2392 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2394 if (buffer[end_offset] != '\0')
2396 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2397 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2398 if (buffer[end_offset +1] != '\0')
2400 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2401 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2406 DestroyWindow(hwndRichEdit);
2407 hwndRichEdit = NULL;
2410 DestroyWindow(parent);
2413 static void test_EM_SCROLL(void)
2415 int i, j;
2416 int r; /* return value */
2417 int expr; /* expected return value */
2418 HWND hwndRichEdit = new_richedit(NULL);
2419 int y_before, y_after; /* units of lines of text */
2421 /* test a richedit box containing a single line of text */
2422 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2423 expr = 0x00010000;
2424 for (i = 0; i < 4; i++) {
2425 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2427 r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2428 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2429 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2430 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2431 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2432 "(i == %d)\n", y_after, i);
2436 * test a richedit box that will scroll. There are two general
2437 * cases: the case without any long lines and the case with a long
2438 * line.
2440 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2441 if (i == 0)
2442 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2443 else
2444 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2445 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2446 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2447 "LONG LINE \nb\nc\nd\ne");
2448 for (j = 0; j < 12; j++) /* reset scroll position to top */
2449 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2451 /* get first visible line */
2452 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2453 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2455 /* get new current first visible line */
2456 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2458 ok(((r & 0xffffff00) == 0x00010000) &&
2459 ((r & 0x000000ff) != 0x00000000),
2460 "EM_SCROLL page down didn't scroll by a small positive number of "
2461 "lines (r == 0x%08x)\n", r);
2462 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2463 "(line %d scrolled to line %d\n", y_before, y_after);
2465 y_before = y_after;
2467 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2468 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2469 ok(((r & 0xffffff00) == 0x0001ff00),
2470 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2471 "(r == 0x%08x)\n", r);
2472 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2473 "%d scrolled to line %d\n", y_before, y_after);
2475 y_before = y_after;
2477 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2479 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2481 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2482 "(r == 0x%08x)\n", r);
2483 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2484 "1 line (%d scrolled to %d)\n", y_before, y_after);
2486 y_before = y_after;
2488 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2490 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2492 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2493 "(r == 0x%08x)\n", r);
2494 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2495 "line (%d scrolled to %d)\n", y_before, y_after);
2497 y_before = y_after;
2499 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2500 SB_LINEUP, 0); /* lineup beyond top */
2502 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2504 ok(r == 0x00010000,
2505 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2506 ok(y_before == y_after,
2507 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2509 y_before = y_after;
2511 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2512 SB_PAGEUP, 0);/*page up beyond top */
2514 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2516 ok(r == 0x00010000,
2517 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2518 ok(y_before == y_after,
2519 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2521 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2522 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2523 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2524 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2525 SB_PAGEDOWN, 0); /* page down beyond bot */
2526 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2528 ok(r == 0x00010000,
2529 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2530 ok(y_before == y_after,
2531 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2532 y_before, y_after);
2534 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2535 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2536 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2538 ok(r == 0x00010000,
2539 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2540 ok(y_before == y_after,
2541 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2542 y_before, y_after);
2544 DestroyWindow(hwndRichEdit);
2547 static unsigned int recursionLevel = 0;
2548 static unsigned int WM_SIZE_recursionLevel = 0;
2549 static BOOL bailedOutOfRecursion = FALSE;
2550 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2552 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2554 LRESULT r;
2556 if (bailedOutOfRecursion) return 0;
2557 if (recursionLevel >= 32) {
2558 bailedOutOfRecursion = TRUE;
2559 return 0;
2562 recursionLevel++;
2563 switch (message) {
2564 case WM_SIZE:
2565 WM_SIZE_recursionLevel++;
2566 r = richeditProc(hwnd, message, wParam, lParam);
2567 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2568 ShowScrollBar(hwnd, SB_VERT, TRUE);
2569 WM_SIZE_recursionLevel--;
2570 break;
2571 default:
2572 r = richeditProc(hwnd, message, wParam, lParam);
2573 break;
2575 recursionLevel--;
2576 return r;
2579 static void test_scrollbar_visibility(void)
2581 HWND hwndRichEdit;
2582 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2583 SCROLLINFO si;
2584 WNDCLASSA cls;
2585 BOOL r;
2587 /* These tests show that richedit should temporarily refrain from automatically
2588 hiding or showing its scrollbars (vertical at least) when an explicit request
2589 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2590 Some applications depend on forced showing (when otherwise richedit would
2591 hide the vertical scrollbar) and are thrown on an endless recursive loop
2592 if richedit auto-hides the scrollbar again. Apparently they never heard of
2593 the ES_DISABLENOSCROLL style... */
2595 hwndRichEdit = new_richedit(NULL);
2597 /* Test default scrollbar visibility behavior */
2598 memset(&si, 0, sizeof(si));
2599 si.cbSize = sizeof(si);
2600 si.fMask = SIF_PAGE | SIF_RANGE;
2601 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2602 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2603 "Vertical scrollbar is visible, should be invisible.\n");
2604 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2605 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2606 si.nPage, si.nMin, si.nMax);
2608 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2609 memset(&si, 0, sizeof(si));
2610 si.cbSize = sizeof(si);
2611 si.fMask = SIF_PAGE | SIF_RANGE;
2612 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2613 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2614 "Vertical scrollbar is visible, should be invisible.\n");
2615 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2616 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2617 si.nPage, si.nMin, si.nMax);
2619 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2620 memset(&si, 0, sizeof(si));
2621 si.cbSize = sizeof(si);
2622 si.fMask = SIF_PAGE | SIF_RANGE;
2623 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2624 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2625 "Vertical scrollbar is invisible, should be visible.\n");
2626 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2627 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2628 si.nPage, si.nMin, si.nMax);
2630 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2631 even though it hides the scrollbar */
2632 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2633 memset(&si, 0, sizeof(si));
2634 si.cbSize = sizeof(si);
2635 si.fMask = SIF_PAGE | SIF_RANGE;
2636 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2637 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2638 "Vertical scrollbar is visible, should be invisible.\n");
2639 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2640 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2641 si.nPage, si.nMin, si.nMax);
2643 /* Setting non-scrolling text again does *not* reset scrollbar range */
2644 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2645 memset(&si, 0, sizeof(si));
2646 si.cbSize = sizeof(si);
2647 si.fMask = SIF_PAGE | SIF_RANGE;
2648 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2649 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2650 "Vertical scrollbar is visible, should be invisible.\n");
2651 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2652 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2653 si.nPage, si.nMin, si.nMax);
2655 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2656 memset(&si, 0, sizeof(si));
2657 si.cbSize = sizeof(si);
2658 si.fMask = SIF_PAGE | SIF_RANGE;
2659 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2660 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2661 "Vertical scrollbar is visible, should be invisible.\n");
2662 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2663 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2664 si.nPage, si.nMin, si.nMax);
2666 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2667 memset(&si, 0, sizeof(si));
2668 si.cbSize = sizeof(si);
2669 si.fMask = SIF_PAGE | SIF_RANGE;
2670 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2671 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2672 "Vertical scrollbar is visible, should be invisible.\n");
2673 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2674 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2675 si.nPage, si.nMin, si.nMax);
2677 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2678 memset(&si, 0, sizeof(si));
2679 si.cbSize = sizeof(si);
2680 si.fMask = SIF_PAGE | SIF_RANGE;
2681 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2682 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2683 "Vertical scrollbar is visible, should be invisible.\n");
2684 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2685 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2686 si.nPage, si.nMin, si.nMax);
2688 DestroyWindow(hwndRichEdit);
2690 /* Test again, with ES_DISABLENOSCROLL style */
2691 hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2693 /* Test default scrollbar visibility behavior */
2694 memset(&si, 0, sizeof(si));
2695 si.cbSize = sizeof(si);
2696 si.fMask = SIF_PAGE | SIF_RANGE;
2697 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2698 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2699 "Vertical scrollbar is invisible, should be visible.\n");
2700 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2701 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2702 si.nPage, si.nMin, si.nMax);
2704 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2705 memset(&si, 0, sizeof(si));
2706 si.cbSize = sizeof(si);
2707 si.fMask = SIF_PAGE | SIF_RANGE;
2708 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2709 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2710 "Vertical scrollbar is invisible, should be visible.\n");
2711 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2712 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2713 si.nPage, si.nMin, si.nMax);
2715 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2716 memset(&si, 0, sizeof(si));
2717 si.cbSize = sizeof(si);
2718 si.fMask = SIF_PAGE | SIF_RANGE;
2719 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2720 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2721 "Vertical scrollbar is invisible, should be visible.\n");
2722 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2723 "reported page/range is %d (%d..%d)\n",
2724 si.nPage, si.nMin, si.nMax);
2726 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2727 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2728 memset(&si, 0, sizeof(si));
2729 si.cbSize = sizeof(si);
2730 si.fMask = SIF_PAGE | SIF_RANGE;
2731 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2732 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2733 "Vertical scrollbar is invisible, should be visible.\n");
2734 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2735 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2736 si.nPage, si.nMin, si.nMax);
2738 /* Setting non-scrolling text again does *not* reset scrollbar range */
2739 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2740 memset(&si, 0, sizeof(si));
2741 si.cbSize = sizeof(si);
2742 si.fMask = SIF_PAGE | SIF_RANGE;
2743 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2744 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2745 "Vertical scrollbar is invisible, should be visible.\n");
2746 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2747 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2748 si.nPage, si.nMin, si.nMax);
2750 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2751 memset(&si, 0, sizeof(si));
2752 si.cbSize = sizeof(si);
2753 si.fMask = SIF_PAGE | SIF_RANGE;
2754 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2755 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2756 "Vertical scrollbar is invisible, should be visible.\n");
2757 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2758 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2759 si.nPage, si.nMin, si.nMax);
2761 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2762 memset(&si, 0, sizeof(si));
2763 si.cbSize = sizeof(si);
2764 si.fMask = SIF_PAGE | SIF_RANGE;
2765 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2766 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2767 "Vertical scrollbar is invisible, should be visible.\n");
2768 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2769 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2770 si.nPage, si.nMin, si.nMax);
2772 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2773 memset(&si, 0, sizeof(si));
2774 si.cbSize = sizeof(si);
2775 si.fMask = SIF_PAGE | SIF_RANGE;
2776 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2777 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2778 "Vertical scrollbar is invisible, should be visible.\n");
2779 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2780 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2781 si.nPage, si.nMin, si.nMax);
2783 DestroyWindow(hwndRichEdit);
2785 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2786 hwndRichEdit = new_richedit(NULL);
2788 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2789 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2790 memset(&si, 0, sizeof(si));
2791 si.cbSize = sizeof(si);
2792 si.fMask = SIF_PAGE | SIF_RANGE;
2793 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2794 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2795 "Vertical scrollbar is invisible, should be visible.\n");
2796 todo_wine {
2797 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2798 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2799 si.nPage, si.nMin, si.nMax);
2802 /* Ditto, see above */
2803 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2804 memset(&si, 0, sizeof(si));
2805 si.cbSize = sizeof(si);
2806 si.fMask = SIF_PAGE | SIF_RANGE;
2807 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2808 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2809 "Vertical scrollbar is invisible, should be visible.\n");
2810 todo_wine {
2811 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2812 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2813 si.nPage, si.nMin, si.nMax);
2816 /* Ditto, see above */
2817 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2818 memset(&si, 0, sizeof(si));
2819 si.cbSize = sizeof(si);
2820 si.fMask = SIF_PAGE | SIF_RANGE;
2821 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2822 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2823 "Vertical scrollbar is invisible, should be visible.\n");
2824 todo_wine {
2825 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2826 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2827 si.nPage, si.nMin, si.nMax);
2830 /* Ditto, see above */
2831 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2832 memset(&si, 0, sizeof(si));
2833 si.cbSize = sizeof(si);
2834 si.fMask = SIF_PAGE | SIF_RANGE;
2835 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2836 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2837 "Vertical scrollbar is invisible, should be visible.\n");
2838 todo_wine {
2839 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2840 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2841 si.nPage, si.nMin, si.nMax);
2844 /* Ditto, see above */
2845 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2846 memset(&si, 0, sizeof(si));
2847 si.cbSize = sizeof(si);
2848 si.fMask = SIF_PAGE | SIF_RANGE;
2849 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2850 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2851 "Vertical scrollbar is invisible, should be visible.\n");
2852 todo_wine {
2853 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2854 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2855 si.nPage, si.nMin, si.nMax);
2858 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
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,
2867 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2868 si.nPage, si.nMin, si.nMax);
2870 DestroyWindow(hwndRichEdit);
2872 hwndRichEdit = new_richedit(NULL);
2874 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2875 memset(&si, 0, sizeof(si));
2876 si.cbSize = sizeof(si);
2877 si.fMask = SIF_PAGE | SIF_RANGE;
2878 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2879 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2880 "Vertical scrollbar is visible, should be invisible.\n");
2881 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2882 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2883 si.nPage, si.nMin, si.nMax);
2885 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2886 memset(&si, 0, sizeof(si));
2887 si.cbSize = sizeof(si);
2888 si.fMask = SIF_PAGE | SIF_RANGE;
2889 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2890 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2891 "Vertical scrollbar is visible, should be invisible.\n");
2892 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2893 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2894 si.nPage, si.nMin, si.nMax);
2896 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2897 memset(&si, 0, sizeof(si));
2898 si.cbSize = sizeof(si);
2899 si.fMask = SIF_PAGE | SIF_RANGE;
2900 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2901 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2902 "Vertical scrollbar is visible, should be invisible.\n");
2903 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2904 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2905 si.nPage, si.nMin, si.nMax);
2907 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2908 memset(&si, 0, sizeof(si));
2909 si.cbSize = sizeof(si);
2910 si.fMask = SIF_PAGE | SIF_RANGE;
2911 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2912 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2913 "Vertical scrollbar is visible, should be invisible.\n");
2914 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2915 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2916 si.nPage, si.nMin, si.nMax);
2918 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2919 memset(&si, 0, sizeof(si));
2920 si.cbSize = sizeof(si);
2921 si.fMask = SIF_PAGE | SIF_RANGE;
2922 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2923 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2924 "Vertical scrollbar is invisible, should be visible.\n");
2925 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2926 "reported page/range is %d (%d..%d)\n",
2927 si.nPage, si.nMin, si.nMax);
2929 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2930 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2931 memset(&si, 0, sizeof(si));
2932 si.cbSize = sizeof(si);
2933 si.fMask = SIF_PAGE | SIF_RANGE;
2934 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2935 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2936 "Vertical scrollbar is visible, should be invisible.\n");
2937 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2938 "reported page/range is %d (%d..%d)\n",
2939 si.nPage, si.nMin, si.nMax);
2941 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2942 memset(&si, 0, sizeof(si));
2943 si.cbSize = sizeof(si);
2944 si.fMask = SIF_PAGE | SIF_RANGE;
2945 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2946 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2947 "Vertical scrollbar is visible, should be invisible.\n");
2948 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2949 "reported page/range is %d (%d..%d)\n",
2950 si.nPage, si.nMin, si.nMax);
2952 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2953 EM_SCROLL will make visible any forcefully invisible scrollbar */
2954 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2955 memset(&si, 0, sizeof(si));
2956 si.cbSize = sizeof(si);
2957 si.fMask = SIF_PAGE | SIF_RANGE;
2958 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2959 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2960 "Vertical scrollbar is invisible, should be visible.\n");
2961 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2962 "reported page/range is %d (%d..%d)\n",
2963 si.nPage, si.nMin, si.nMax);
2965 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2966 memset(&si, 0, sizeof(si));
2967 si.cbSize = sizeof(si);
2968 si.fMask = SIF_PAGE | SIF_RANGE;
2969 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2970 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2971 "Vertical scrollbar is visible, should be invisible.\n");
2972 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2973 "reported page/range is %d (%d..%d)\n",
2974 si.nPage, si.nMin, si.nMax);
2976 /* Again, EM_SCROLL, with SB_LINEUP */
2977 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2978 memset(&si, 0, sizeof(si));
2979 si.cbSize = sizeof(si);
2980 si.fMask = SIF_PAGE | SIF_RANGE;
2981 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2982 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2983 "Vertical scrollbar is invisible, should be visible.\n");
2984 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2985 "reported page/range is %d (%d..%d)\n",
2986 si.nPage, si.nMin, si.nMax);
2988 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2989 memset(&si, 0, sizeof(si));
2990 si.cbSize = sizeof(si);
2991 si.fMask = SIF_PAGE | SIF_RANGE;
2992 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2993 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2994 "Vertical scrollbar is visible, should be invisible.\n");
2995 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2996 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2997 si.nPage, si.nMin, si.nMax);
2999 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3000 memset(&si, 0, sizeof(si));
3001 si.cbSize = sizeof(si);
3002 si.fMask = SIF_PAGE | SIF_RANGE;
3003 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3004 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3005 "Vertical scrollbar is invisible, should be visible.\n");
3006 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3007 "reported page/range is %d (%d..%d)\n",
3008 si.nPage, si.nMin, si.nMax);
3010 DestroyWindow(hwndRichEdit);
3013 /* Test behavior with explicit visibility request, using SetWindowLongA()() */
3014 hwndRichEdit = new_richedit(NULL);
3016 #define ENABLE_WS_VSCROLL(hwnd) \
3017 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
3018 #define DISABLE_WS_VSCROLL(hwnd) \
3019 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
3021 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3022 ENABLE_WS_VSCROLL(hwndRichEdit);
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 /* Ditto, see above */
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 invisible, should be visible.\n");
3041 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3042 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3043 si.nPage, si.nMin, si.nMax);
3045 /* Ditto, see above */
3046 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3047 memset(&si, 0, sizeof(si));
3048 si.cbSize = sizeof(si);
3049 si.fMask = SIF_PAGE | SIF_RANGE;
3050 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3051 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3052 "Vertical scrollbar is invisible, should be visible.\n");
3053 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3054 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3055 si.nPage, si.nMin, si.nMax);
3057 /* Ditto, see above */
3058 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3059 memset(&si, 0, sizeof(si));
3060 si.cbSize = sizeof(si);
3061 si.fMask = SIF_PAGE | SIF_RANGE;
3062 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3063 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3064 "Vertical scrollbar is invisible, should be visible.\n");
3065 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3066 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3067 si.nPage, si.nMin, si.nMax);
3069 /* Ditto, see above */
3070 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3071 memset(&si, 0, sizeof(si));
3072 si.cbSize = sizeof(si);
3073 si.fMask = SIF_PAGE | SIF_RANGE;
3074 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3075 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3076 "Vertical scrollbar is invisible, should be visible.\n");
3077 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3078 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3079 si.nPage, si.nMin, si.nMax);
3081 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
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,
3090 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3091 si.nPage, si.nMin, si.nMax);
3093 DestroyWindow(hwndRichEdit);
3095 hwndRichEdit = new_richedit(NULL);
3097 DISABLE_WS_VSCROLL(hwndRichEdit);
3098 memset(&si, 0, sizeof(si));
3099 si.cbSize = sizeof(si);
3100 si.fMask = SIF_PAGE | SIF_RANGE;
3101 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3102 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3103 "Vertical scrollbar is visible, should be invisible.\n");
3104 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3105 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3106 si.nPage, si.nMin, si.nMax);
3108 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3109 memset(&si, 0, sizeof(si));
3110 si.cbSize = sizeof(si);
3111 si.fMask = SIF_PAGE | SIF_RANGE;
3112 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3113 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3114 "Vertical scrollbar is visible, should be invisible.\n");
3115 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3116 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3117 si.nPage, si.nMin, si.nMax);
3119 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3120 memset(&si, 0, sizeof(si));
3121 si.cbSize = sizeof(si);
3122 si.fMask = SIF_PAGE | SIF_RANGE;
3123 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3124 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3125 "Vertical scrollbar is visible, should be invisible.\n");
3126 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3127 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3128 si.nPage, si.nMin, si.nMax);
3130 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3131 memset(&si, 0, sizeof(si));
3132 si.cbSize = sizeof(si);
3133 si.fMask = SIF_PAGE | SIF_RANGE;
3134 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3135 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3136 "Vertical scrollbar is visible, should be invisible.\n");
3137 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3138 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3139 si.nPage, si.nMin, si.nMax);
3141 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3142 memset(&si, 0, sizeof(si));
3143 si.cbSize = sizeof(si);
3144 si.fMask = SIF_PAGE | SIF_RANGE;
3145 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3146 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3147 "Vertical scrollbar is invisible, should be visible.\n");
3148 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3149 "reported page/range is %d (%d..%d)\n",
3150 si.nPage, si.nMin, si.nMax);
3152 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3153 DISABLE_WS_VSCROLL(hwndRichEdit);
3154 memset(&si, 0, sizeof(si));
3155 si.cbSize = sizeof(si);
3156 si.fMask = SIF_PAGE | SIF_RANGE;
3157 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3158 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3159 "Vertical scrollbar is visible, should be invisible.\n");
3160 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3161 "reported page/range is %d (%d..%d)\n",
3162 si.nPage, si.nMin, si.nMax);
3164 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3165 memset(&si, 0, sizeof(si));
3166 si.cbSize = sizeof(si);
3167 si.fMask = SIF_PAGE | SIF_RANGE;
3168 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3169 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3170 "Vertical scrollbar is visible, should be invisible.\n");
3171 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3172 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3173 si.nPage, si.nMin, si.nMax);
3175 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3176 memset(&si, 0, sizeof(si));
3177 si.cbSize = sizeof(si);
3178 si.fMask = SIF_PAGE | SIF_RANGE;
3179 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3180 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3181 "Vertical scrollbar is invisible, should be visible.\n");
3182 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3183 "reported page/range is %d (%d..%d)\n",
3184 si.nPage, si.nMin, si.nMax);
3186 DISABLE_WS_VSCROLL(hwndRichEdit);
3187 memset(&si, 0, sizeof(si));
3188 si.cbSize = sizeof(si);
3189 si.fMask = SIF_PAGE | SIF_RANGE;
3190 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3191 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3192 "Vertical scrollbar is visible, should be invisible.\n");
3193 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3194 "reported page/range is %d (%d..%d)\n",
3195 si.nPage, si.nMin, si.nMax);
3197 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3198 EM_SCROLL will make visible any forcefully invisible scrollbar */
3199 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3200 memset(&si, 0, sizeof(si));
3201 si.cbSize = sizeof(si);
3202 si.fMask = SIF_PAGE | SIF_RANGE;
3203 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3204 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3205 "Vertical scrollbar is invisible, should be visible.\n");
3206 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3207 "reported page/range is %d (%d..%d)\n",
3208 si.nPage, si.nMin, si.nMax);
3210 DISABLE_WS_VSCROLL(hwndRichEdit);
3211 memset(&si, 0, sizeof(si));
3212 si.cbSize = sizeof(si);
3213 si.fMask = SIF_PAGE | SIF_RANGE;
3214 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3215 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3216 "Vertical scrollbar is visible, should be invisible.\n");
3217 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3218 "reported page/range is %d (%d..%d)\n",
3219 si.nPage, si.nMin, si.nMax);
3221 /* Again, EM_SCROLL, with SB_LINEUP */
3222 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3223 memset(&si, 0, sizeof(si));
3224 si.cbSize = sizeof(si);
3225 si.fMask = SIF_PAGE | SIF_RANGE;
3226 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3227 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3228 "Vertical scrollbar is invisible, should be visible.\n");
3229 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3230 "reported page/range is %d (%d..%d)\n",
3231 si.nPage, si.nMin, si.nMax);
3233 DestroyWindow(hwndRichEdit);
3235 /* This window proc models what is going on with Corman Lisp 3.0.
3236 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3237 force the scrollbar into visibility. Recursion should NOT happen
3238 as a result of this action.
3240 r = GetClassInfoA(NULL, RICHEDIT_CLASS20A, &cls);
3241 if (r) {
3242 richeditProc = cls.lpfnWndProc;
3243 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3244 cls.lpszClassName = "RicheditStupidOverride";
3245 if(!RegisterClassA(&cls)) assert(0);
3247 recursionLevel = 0;
3248 WM_SIZE_recursionLevel = 0;
3249 bailedOutOfRecursion = FALSE;
3250 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3251 ok(!bailedOutOfRecursion,
3252 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3254 recursionLevel = 0;
3255 WM_SIZE_recursionLevel = 0;
3256 bailedOutOfRecursion = FALSE;
3257 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3258 ok(!bailedOutOfRecursion,
3259 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3261 /* Unblock window in order to process WM_DESTROY */
3262 recursionLevel = 0;
3263 bailedOutOfRecursion = FALSE;
3264 WM_SIZE_recursionLevel = 0;
3265 DestroyWindow(hwndRichEdit);
3269 static void test_EM_SETUNDOLIMIT(void)
3271 /* cases we test for:
3272 * default behaviour - limiting at 100 undo's
3273 * undo disabled - setting a limit of 0
3274 * undo limited - undo limit set to some to some number, like 2
3275 * bad input - sending a negative number should default to 100 undo's */
3277 HWND hwndRichEdit = new_richedit(NULL);
3278 CHARRANGE cr;
3279 int i;
3280 int result;
3282 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3283 cr.cpMin = 0;
3284 cr.cpMax = 1;
3285 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3286 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3287 also, multiple pastes don't combine like WM_CHAR would */
3288 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3290 /* first case - check the default */
3291 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3292 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3293 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3294 for (i=0; i<100; i++) /* Undo 100 of them */
3295 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3296 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3297 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3299 /* second case - cannot undo */
3300 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3301 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3302 SendMessageA(hwndRichEdit,
3303 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3304 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3305 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3307 /* third case - set it to an arbitrary number */
3308 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3309 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3310 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3311 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3312 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3313 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3314 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3315 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3316 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3317 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3318 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3319 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3320 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3321 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3323 /* fourth case - setting negative numbers should default to 100 undos */
3324 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3325 result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3326 ok (result == 100,
3327 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3329 DestroyWindow(hwndRichEdit);
3332 static void test_ES_PASSWORD(void)
3334 /* This isn't hugely testable, so we're just going to run it through its paces */
3336 HWND hwndRichEdit = new_richedit(NULL);
3337 WCHAR result;
3339 /* First, check the default of a regular control */
3340 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3341 ok (result == 0,
3342 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3344 /* Now, set it to something normal */
3345 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3346 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3347 ok (result == 120,
3348 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3350 /* Now, set it to something odd */
3351 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3352 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3353 ok (result == 1234,
3354 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3355 DestroyWindow(hwndRichEdit);
3358 LONG streamout_written = 0;
3360 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3361 LPBYTE pbBuff,
3362 LONG cb,
3363 LONG *pcb)
3365 char** str = (char**)dwCookie;
3366 *pcb = cb;
3367 if (*pcb > 0) {
3368 memcpy(*str, pbBuff, *pcb);
3369 *str += *pcb;
3371 streamout_written = *pcb;
3372 return 0;
3375 static void test_WM_SETTEXT(void)
3377 HWND hwndRichEdit = new_richedit(NULL);
3378 const char * TestItem1 = "TestSomeText";
3379 const char * TestItem2 = "TestSomeText\r";
3380 const char * TestItem2_after = "TestSomeText\r\n";
3381 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3382 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3383 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3384 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3385 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3386 const char * TestItem5_after = "TestSomeText TestSomeText";
3387 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3388 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3389 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3390 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3392 const char rtftextA[] = "{\\rtf sometext}";
3393 const char urtftextA[] = "{\\urtf sometext}";
3394 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3395 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3396 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3398 char buf[1024] = {0};
3399 WCHAR bufW[1024] = {0};
3400 LRESULT result;
3402 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3403 any solitary \r to be converted to \r\n on return. Properly paired
3404 \r\n are not affected. It also shows that the special sequence \r\r\n
3405 gets converted to a single space.
3408 #define TEST_SETTEXT(a, b) \
3409 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3410 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3411 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3412 ok (result == lstrlenA(buf), \
3413 "WM_GETTEXT returned %ld instead of expected %u\n", \
3414 result, lstrlenA(buf)); \
3415 result = strcmp(b, buf); \
3416 ok(result == 0, \
3417 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3419 TEST_SETTEXT(TestItem1, TestItem1)
3420 TEST_SETTEXT(TestItem2, TestItem2_after)
3421 TEST_SETTEXT(TestItem3, TestItem3_after)
3422 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3423 TEST_SETTEXT(TestItem4, TestItem4_after)
3424 TEST_SETTEXT(TestItem5, TestItem5_after)
3425 TEST_SETTEXT(TestItem6, TestItem6_after)
3426 TEST_SETTEXT(TestItem7, TestItem7_after)
3428 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3429 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3430 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3431 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3432 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3433 DestroyWindow(hwndRichEdit);
3434 #undef TEST_SETTEXT
3436 #define TEST_SETTEXTW(a, b) \
3437 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3438 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3439 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3440 ok (result == lstrlenW(bufW), \
3441 "WM_GETTEXT returned %ld instead of expected %u\n", \
3442 result, lstrlenW(bufW)); \
3443 result = lstrcmpW(b, bufW); \
3444 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3446 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3447 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3448 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3449 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3450 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3451 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3452 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3453 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3454 DestroyWindow(hwndRichEdit);
3455 #undef TEST_SETTEXTW
3458 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3459 resent to the callkack. */
3460 static DWORD CALLBACK test_esCallback_written_1(DWORD_PTR dwCookie,
3461 LPBYTE pbBuff,
3462 LONG cb,
3463 LONG *pcb)
3465 char** str = (char**)dwCookie;
3466 ok(*pcb == cb || *pcb == 0, "cb %d, *pcb %d\n", cb, *pcb);
3467 *pcb = 0;
3468 if (cb > 0) {
3469 memcpy(*str, pbBuff, cb);
3470 *str += cb;
3471 *pcb = 1;
3473 return 0;
3476 static int count_pars(const char *buf)
3478 const char *p = buf;
3479 int count = 0;
3480 while ((p = strstr( p, "\\par" )) != NULL)
3482 if (!isalpha( p[4] ))
3483 count++;
3484 p++;
3486 return count;
3489 static void test_EM_STREAMOUT(void)
3491 HWND hwndRichEdit = new_richedit(NULL);
3492 int r;
3493 EDITSTREAM es;
3494 char buf[1024] = {0};
3495 char * p;
3496 LRESULT result;
3498 const char * TestItem1 = "TestSomeText";
3499 const char * TestItem2 = "TestSomeText\r";
3500 const char * TestItem3 = "TestSomeText\r\n";
3502 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3503 p = buf;
3504 es.dwCookie = (DWORD_PTR)&p;
3505 es.dwError = 0;
3506 es.pfnCallback = test_WM_SETTEXT_esCallback;
3507 memset(buf, 0, sizeof(buf));
3508 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3509 r = strlen(buf);
3510 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3511 ok(strcmp(buf, TestItem1) == 0,
3512 "streamed text different, got %s\n", buf);
3513 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3515 /* RTF mode writes the final end of para \r if it's part of the selection */
3516 p = buf;
3517 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3518 ok (count_pars(buf) == 1, "got %s\n", buf);
3519 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3520 p = buf;
3521 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 12);
3522 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3523 ok (count_pars(buf) == 0, "got %s\n", buf);
3524 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3525 p = buf;
3526 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3527 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3528 ok (count_pars(buf) == 1, "got %s\n", buf);
3529 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3531 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3532 p = buf;
3533 es.dwCookie = (DWORD_PTR)&p;
3534 es.dwError = 0;
3535 es.pfnCallback = test_WM_SETTEXT_esCallback;
3536 memset(buf, 0, sizeof(buf));
3537 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3538 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3539 r = strlen(buf);
3540 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3541 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3542 ok(strcmp(buf, TestItem3) == 0,
3543 "streamed text different from, got %s\n", buf);
3545 /* And again RTF mode writes the final end of para \r if it's part of the selection */
3546 p = buf;
3547 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3548 ok (count_pars(buf) == 2, "got %s\n", buf);
3549 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3550 p = buf;
3551 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 13);
3552 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3553 ok (count_pars(buf) == 1, "got %s\n", buf);
3554 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3555 p = buf;
3556 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3557 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3558 ok (count_pars(buf) == 2, "got %s\n", buf);
3559 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3561 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3562 p = buf;
3563 es.dwCookie = (DWORD_PTR)&p;
3564 es.dwError = 0;
3565 es.pfnCallback = test_WM_SETTEXT_esCallback;
3566 memset(buf, 0, sizeof(buf));
3567 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3568 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3569 r = strlen(buf);
3570 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3571 ok(strcmp(buf, TestItem3) == 0,
3572 "streamed text different, got %s\n", buf);
3574 /* Use a callback that sets *pcb to one */
3575 p = buf;
3576 es.dwCookie = (DWORD_PTR)&p;
3577 es.dwError = 0;
3578 es.pfnCallback = test_esCallback_written_1;
3579 memset(buf, 0, sizeof(buf));
3580 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3581 r = strlen(buf);
3582 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3583 ok(strcmp(buf, TestItem3) == 0,
3584 "streamed text different, got %s\n", buf);
3585 ok(result == 0, "got %ld expected 0\n", result);
3588 DestroyWindow(hwndRichEdit);
3591 static void test_EM_STREAMOUT_FONTTBL(void)
3593 HWND hwndRichEdit = new_richedit(NULL);
3594 EDITSTREAM es;
3595 char buf[1024] = {0};
3596 char * p;
3597 char * fontTbl;
3598 int brackCount;
3600 const char * TestItem = "TestSomeText";
3602 /* fills in the richedit control with some text */
3603 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
3605 /* streams out the text in rtf format */
3606 p = buf;
3607 es.dwCookie = (DWORD_PTR)&p;
3608 es.dwError = 0;
3609 es.pfnCallback = test_WM_SETTEXT_esCallback;
3610 memset(buf, 0, sizeof(buf));
3611 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3613 /* scans for \fonttbl, error if not found */
3614 fontTbl = strstr(buf, "\\fonttbl");
3615 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3616 if(fontTbl)
3618 /* scans for terminating closing bracket */
3619 brackCount = 1;
3620 while(*fontTbl && brackCount)
3622 if(*fontTbl == '{')
3623 brackCount++;
3624 else if(*fontTbl == '}')
3625 brackCount--;
3626 fontTbl++;
3628 /* checks whether closing bracket is ok */
3629 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3630 if(!brackCount)
3632 /* char before closing fonttbl block should be a closed bracket */
3633 fontTbl -= 2;
3634 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3636 /* char after fonttbl block should be a crlf */
3637 fontTbl += 2;
3638 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3641 DestroyWindow(hwndRichEdit);
3645 static void test_EM_SETTEXTEX(void)
3647 HWND hwndRichEdit, parent;
3648 SCROLLINFO si;
3649 int sel_start, sel_end;
3650 SETTEXTEX setText;
3651 GETTEXTEX getText;
3652 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3653 'S', 'o', 'm', 'e',
3654 'T', 'e', 'x', 't', 0};
3655 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3656 't', 'S', 'o', 'm',
3657 'e', 'T', 'e', 'x',
3658 't', 't', 'S', 'o',
3659 'm', 'e', 'T', 'e',
3660 'x', 't', 0};
3661 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3662 '\r','t','S','o','m','e','T','e','x','t',0};
3663 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3664 'S', 'o', 'm', 'e',
3665 'T', 'e', 'x', 't',
3666 '\r', 0};
3667 const char * TestItem2_after = "TestSomeText\r\n";
3668 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3669 'S', 'o', 'm', 'e',
3670 'T', 'e', 'x', 't',
3671 '\r','\n','\r','\n', 0};
3672 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3673 'S', 'o', 'm', 'e',
3674 'T', 'e', 'x', 't',
3675 '\n','\n', 0};
3676 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3677 'S', 'o', 'm', 'e',
3678 'T', 'e', 'x', 't',
3679 '\r','\r', 0};
3680 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3681 'S', 'o', 'm', 'e',
3682 'T', 'e', 'x', 't',
3683 '\r','\r','\n','\r',
3684 '\n', 0};
3685 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3686 'S', 'o', 'm', 'e',
3687 'T', 'e', 'x', 't',
3688 ' ','\r', 0};
3689 #define MAX_BUF_LEN 1024
3690 WCHAR buf[MAX_BUF_LEN];
3691 char bufACP[MAX_BUF_LEN];
3692 char * p;
3693 int result;
3694 CHARRANGE cr;
3695 EDITSTREAM es;
3696 WNDCLASSA cls;
3698 /* Test the scroll position with and without a parent window.
3700 * For some reason the scroll position is 0 after EM_SETTEXTEX
3701 * with the ST_SELECTION flag only when the control has a parent
3702 * window, even though the selection is at the end. */
3703 cls.style = 0;
3704 cls.lpfnWndProc = DefWindowProcA;
3705 cls.cbClsExtra = 0;
3706 cls.cbWndExtra = 0;
3707 cls.hInstance = GetModuleHandleA(0);
3708 cls.hIcon = 0;
3709 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
3710 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3711 cls.lpszMenuName = NULL;
3712 cls.lpszClassName = "ParentTestClass";
3713 if(!RegisterClassA(&cls)) assert(0);
3715 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3716 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3717 ok (parent != 0, "Failed to create parent window\n");
3719 hwndRichEdit = CreateWindowExA(0,
3720 RICHEDIT_CLASS20A, NULL,
3721 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3722 0, 0, 200, 60, parent, NULL,
3723 hmoduleRichEdit, NULL);
3725 setText.codepage = CP_ACP;
3726 setText.flags = ST_SELECTION;
3727 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3728 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3729 todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
3730 si.cbSize = sizeof(si);
3731 si.fMask = SIF_ALL;
3732 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3733 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3734 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3735 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3736 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3738 DestroyWindow(parent);
3740 /* Test without a parent window */
3741 hwndRichEdit = new_richedit(NULL);
3742 setText.codepage = CP_ACP;
3743 setText.flags = ST_SELECTION;
3744 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3745 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3746 todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
3747 si.cbSize = sizeof(si);
3748 si.fMask = SIF_ALL;
3749 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3750 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3751 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3752 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3753 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3755 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3756 * but this time it is because the selection is at the beginning. */
3757 setText.codepage = CP_ACP;
3758 setText.flags = ST_DEFAULT;
3759 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3760 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3761 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3762 si.cbSize = sizeof(si);
3763 si.fMask = SIF_ALL;
3764 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3765 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3766 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3767 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3768 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3770 setText.codepage = 1200; /* no constant for unicode */
3771 getText.codepage = 1200; /* no constant for unicode */
3772 getText.cb = MAX_BUF_LEN;
3773 getText.flags = GT_DEFAULT;
3774 getText.lpDefaultChar = NULL;
3775 getText.lpUsedDefChar = NULL;
3777 setText.flags = 0;
3778 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3779 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3780 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3781 ok(lstrcmpW(buf, TestItem1) == 0,
3782 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3784 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3785 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3787 setText.codepage = 1200; /* no constant for unicode */
3788 getText.codepage = 1200; /* no constant for unicode */
3789 getText.cb = MAX_BUF_LEN;
3790 getText.flags = GT_DEFAULT;
3791 getText.lpDefaultChar = NULL;
3792 getText.lpUsedDefChar = NULL;
3793 setText.flags = 0;
3794 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
3795 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3796 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3797 ok(lstrcmpW(buf, TestItem2) == 0,
3798 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3800 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3801 SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3802 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3803 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3805 /* Baseline test for just-enough buffer space for string */
3806 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3807 getText.codepage = 1200; /* no constant for unicode */
3808 getText.flags = GT_DEFAULT;
3809 getText.lpDefaultChar = NULL;
3810 getText.lpUsedDefChar = NULL;
3811 memset(buf, 0, MAX_BUF_LEN);
3812 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3813 ok(lstrcmpW(buf, TestItem2) == 0,
3814 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3816 /* When there is enough space for one character, but not both, of the CRLF
3817 pair at the end of the string, the CR is not copied at all. That is,
3818 the caller must not see CRLF pairs truncated to CR at the end of the
3819 string.
3821 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3822 getText.codepage = 1200; /* no constant for unicode */
3823 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3824 getText.lpDefaultChar = NULL;
3825 getText.lpUsedDefChar = NULL;
3826 memset(buf, 0, MAX_BUF_LEN);
3827 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3828 ok(lstrcmpW(buf, TestItem1) == 0,
3829 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3832 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3833 setText.codepage = 1200; /* no constant for unicode */
3834 getText.codepage = 1200; /* no constant for unicode */
3835 getText.cb = MAX_BUF_LEN;
3836 getText.flags = GT_DEFAULT;
3837 getText.lpDefaultChar = NULL;
3838 getText.lpUsedDefChar = NULL;
3839 setText.flags = 0;
3840 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
3841 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3842 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3843 ok(lstrcmpW(buf, TestItem3_after) == 0,
3844 "EM_SETTEXTEX did not convert properly\n");
3846 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3847 setText.codepage = 1200; /* no constant for unicode */
3848 getText.codepage = 1200; /* no constant for unicode */
3849 getText.cb = MAX_BUF_LEN;
3850 getText.flags = GT_DEFAULT;
3851 getText.lpDefaultChar = NULL;
3852 getText.lpUsedDefChar = NULL;
3853 setText.flags = 0;
3854 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
3855 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3856 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3857 ok(lstrcmpW(buf, TestItem3_after) == 0,
3858 "EM_SETTEXTEX did not convert properly\n");
3860 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3861 setText.codepage = 1200; /* no constant for unicode */
3862 getText.codepage = 1200; /* no constant for unicode */
3863 getText.cb = MAX_BUF_LEN;
3864 getText.flags = GT_DEFAULT;
3865 getText.lpDefaultChar = NULL;
3866 getText.lpUsedDefChar = NULL;
3867 setText.flags = 0;
3868 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
3869 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3870 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3871 ok(lstrcmpW(buf, TestItem4_after) == 0,
3872 "EM_SETTEXTEX did not convert properly\n");
3874 /* !ST_SELECTION && Unicode && !\rtf */
3875 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3876 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3878 ok (result == 1,
3879 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3880 ok(!buf[0], "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3882 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3883 setText.flags = 0;
3884 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3885 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3886 /* select some text */
3887 cr.cpMax = 1;
3888 cr.cpMin = 3;
3889 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3890 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3891 setText.flags = ST_SELECTION;
3892 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3893 ok(result == 0,
3894 "EM_SETTEXTEX with NULL lParam to replace selection"
3895 " with no text should return 0. Got %i\n",
3896 result);
3898 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3899 setText.flags = 0;
3900 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3901 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3902 /* select some text */
3903 cr.cpMax = 1;
3904 cr.cpMin = 3;
3905 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3906 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3907 setText.flags = ST_SELECTION;
3908 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3909 /* get text */
3910 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3911 ok(result == lstrlenW(TestItem1),
3912 "EM_SETTEXTEX with NULL lParam to replace selection"
3913 " with no text should return 0. Got %i\n",
3914 result);
3915 ok(lstrlenW(buf) == 22,
3916 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3917 lstrlenW(buf) );
3919 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3920 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
3921 p = (char *)buf;
3922 es.dwCookie = (DWORD_PTR)&p;
3923 es.dwError = 0;
3924 es.pfnCallback = test_WM_SETTEXT_esCallback;
3925 memset(buf, 0, sizeof(buf));
3926 SendMessageA(hwndRichEdit, EM_STREAMOUT,
3927 (WPARAM)(SF_RTF), (LPARAM)&es);
3928 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3930 /* !ST_SELECTION && !Unicode && \rtf */
3931 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3932 getText.codepage = 1200; /* no constant for unicode */
3933 getText.cb = MAX_BUF_LEN;
3934 getText.flags = GT_DEFAULT;
3935 getText.lpDefaultChar = NULL;
3936 getText.lpUsedDefChar = NULL;
3938 setText.flags = 0;
3939 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
3940 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3941 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3942 ok(lstrcmpW(buf, TestItem1) == 0,
3943 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3945 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3946 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3947 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3948 getText.codepage = CP_ACP;
3949 getText.cb = MAX_BUF_LEN;
3950 getText.flags = GT_DEFAULT;
3951 getText.lpDefaultChar = NULL;
3952 getText.lpUsedDefChar = NULL;
3954 setText.flags = ST_SELECTION;
3955 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3956 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
3957 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3958 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3959 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3961 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3962 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
3963 p = (char *)buf;
3964 es.dwCookie = (DWORD_PTR)&p;
3965 es.dwError = 0;
3966 es.pfnCallback = test_WM_SETTEXT_esCallback;
3967 memset(buf, 0, sizeof(buf));
3968 SendMessageA(hwndRichEdit, EM_STREAMOUT,
3969 (WPARAM)(SF_RTF), (LPARAM)&es);
3970 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3972 /* select some text */
3973 cr.cpMax = 1;
3974 cr.cpMin = 3;
3975 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3977 /* ST_SELECTION && !Unicode && \rtf */
3978 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3979 getText.codepage = 1200; /* no constant for unicode */
3980 getText.cb = MAX_BUF_LEN;
3981 getText.flags = GT_DEFAULT;
3982 getText.lpDefaultChar = NULL;
3983 getText.lpUsedDefChar = NULL;
3985 setText.flags = ST_SELECTION;
3986 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
3987 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3988 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3990 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3991 setText.codepage = 1200; /* no constant for unicode */
3992 getText.codepage = CP_ACP;
3993 getText.cb = MAX_BUF_LEN;
3995 setText.flags = 0;
3996 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
3997 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3999 /* select some text */
4000 cr.cpMax = 1;
4001 cr.cpMin = 3;
4002 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4004 /* ST_SELECTION && !Unicode && !\rtf */
4005 setText.codepage = CP_ACP;
4006 getText.codepage = 1200; /* no constant for unicode */
4007 getText.cb = MAX_BUF_LEN;
4008 getText.flags = GT_DEFAULT;
4009 getText.lpDefaultChar = NULL;
4010 getText.lpUsedDefChar = NULL;
4012 setText.flags = ST_SELECTION;
4013 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
4014 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4015 ok(lstrcmpW(buf, TestItem1alt) == 0,
4016 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
4017 " using ST_SELECTION and non-Unicode\n");
4019 /* Test setting text using rich text format */
4020 setText.flags = 0;
4021 setText.codepage = CP_ACP;
4022 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
4023 getText.codepage = CP_ACP;
4024 getText.cb = MAX_BUF_LEN;
4025 getText.flags = GT_DEFAULT;
4026 getText.lpDefaultChar = NULL;
4027 getText.lpUsedDefChar = NULL;
4028 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4029 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
4031 setText.flags = 0;
4032 setText.codepage = CP_ACP;
4033 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
4034 getText.codepage = CP_ACP;
4035 getText.cb = MAX_BUF_LEN;
4036 getText.flags = GT_DEFAULT;
4037 getText.lpDefaultChar = NULL;
4038 getText.lpUsedDefChar = NULL;
4039 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4040 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
4042 /* test for utf8 text with BOM */
4043 setText.flags = 0;
4044 setText.codepage = CP_ACP;
4045 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4046 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4047 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4048 result = strcmp(bufACP, "TestUTF8WithBOM");
4049 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4051 setText.flags = 0;
4052 setText.codepage = CP_UTF8;
4053 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4054 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4055 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4056 result = strcmp(bufACP, "TestUTF8WithBOM");
4057 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4059 /* Test multibyte character */
4060 if (!is_lang_japanese)
4061 skip("Skip multibyte character tests on non-Japanese platform\n");
4062 else
4064 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4065 setText.flags = ST_SELECTION;
4066 setText.codepage = CP_ACP;
4067 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4068 todo_wine ok(result == 5, "EM_SETTEXTEX incorrectly returned %d, expected 5\n", result);
4069 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4070 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4071 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4072 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4074 setText.flags = ST_DEFAULT;
4075 setText.codepage = CP_ACP;
4076 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4077 ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4078 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4079 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4080 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4081 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4083 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4084 setText.flags = ST_SELECTION;
4085 setText.codepage = CP_ACP;
4086 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf abc\x8e\xf0}");
4087 todo_wine ok(result == 4, "EM_SETTEXTEX incorrectly returned %d, expected 4\n", result);
4088 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4089 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4090 todo_wine ok(!strcmp(bufACP, "abc\x8e\xf0"),
4091 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4094 DestroyWindow(hwndRichEdit);
4097 static void test_EM_LIMITTEXT(void)
4099 int ret;
4101 HWND hwndRichEdit = new_richedit(NULL);
4103 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
4104 * about setting the length to -1 for multiline edit controls doesn't happen.
4107 /* Don't check default gettextlimit case. That's done in other tests */
4109 /* Set textlimit to 100 */
4110 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
4111 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4112 ok (ret == 100,
4113 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
4115 /* Set textlimit to 0 */
4116 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
4117 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4118 ok (ret == 65536,
4119 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
4121 /* Set textlimit to -1 */
4122 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
4123 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4124 ok (ret == -1,
4125 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
4127 /* Set textlimit to -2 */
4128 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
4129 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4130 ok (ret == -2,
4131 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
4133 DestroyWindow (hwndRichEdit);
4137 static void test_EM_EXLIMITTEXT(void)
4139 int i, selBegin, selEnd, len1, len2;
4140 int result;
4141 char text[1024 + 1];
4142 char buffer[1024 + 1];
4143 int textlimit = 0; /* multiple of 100 */
4144 HWND hwndRichEdit = new_richedit(NULL);
4146 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4147 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
4149 textlimit = 256000;
4150 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4151 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4152 /* set higher */
4153 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4155 textlimit = 1000;
4156 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4157 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4158 /* set lower */
4159 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4161 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4162 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4163 /* default for WParam = 0 */
4164 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4166 textlimit = sizeof(text)-1;
4167 memset(text, 'W', textlimit);
4168 text[sizeof(text)-1] = 0;
4169 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4170 /* maxed out text */
4171 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4173 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4174 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4175 len1 = selEnd - selBegin;
4177 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4178 SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4179 SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4180 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4181 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4182 len2 = selEnd - selBegin;
4184 ok(len1 != len2,
4185 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4186 len1,len2,i);
4188 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4189 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4190 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4191 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4192 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4193 len1 = selEnd - selBegin;
4195 ok(len1 != len2,
4196 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4197 len1,len2,i);
4199 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4200 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4201 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
4202 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4203 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4204 len2 = selEnd - selBegin;
4206 ok(len1 == len2,
4207 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4208 len1,len2,i);
4210 /* set text up to the limit, select all the text, then add a char */
4211 textlimit = 5;
4212 memset(text, 'W', textlimit);
4213 text[textlimit] = 0;
4214 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4215 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4216 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4217 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4218 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4219 result = strcmp(buffer, "A");
4220 ok(0 == result, "got string = \"%s\"\n", buffer);
4222 /* WM_SETTEXT not limited */
4223 textlimit = 10;
4224 memset(text, 'W', textlimit);
4225 text[textlimit] = 0;
4226 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4227 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4228 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4229 i = strlen(buffer);
4230 ok(10 == i, "expected 10 chars\n");
4231 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4232 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4234 /* try inserting more text at end */
4235 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4236 ok(0 == i, "WM_CHAR wasn't processed\n");
4237 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4238 i = strlen(buffer);
4239 ok(10 == i, "expected 10 chars, got %i\n", i);
4240 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4241 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4243 /* try inserting text at beginning */
4244 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4245 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4246 ok(0 == i, "WM_CHAR wasn't processed\n");
4247 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4248 i = strlen(buffer);
4249 ok(10 == i, "expected 10 chars, got %i\n", i);
4250 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4251 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4253 /* WM_CHAR is limited */
4254 textlimit = 1;
4255 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4256 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4257 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4258 ok(0 == i, "WM_CHAR wasn't processed\n");
4259 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4260 ok(0 == i, "WM_CHAR wasn't processed\n");
4261 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4262 i = strlen(buffer);
4263 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4265 DestroyWindow(hwndRichEdit);
4268 static void test_EM_GETLIMITTEXT(void)
4270 int i;
4271 HWND hwndRichEdit = new_richedit(NULL);
4273 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4274 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4276 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4277 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4278 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4280 DestroyWindow(hwndRichEdit);
4283 static void test_WM_SETFONT(void)
4285 /* There is no invalid input or error conditions for this function.
4286 * NULL wParam and lParam just fall back to their default values
4287 * It should be noted that even if you use a gibberish name for your fonts
4288 * here, it will still work because the name is stored. They will display as
4289 * System, but will report their name to be whatever they were created as */
4291 HWND hwndRichEdit = new_richedit(NULL);
4292 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4293 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4294 FF_DONTCARE, "Marlett");
4295 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4296 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4297 FF_DONTCARE, "MS Sans Serif");
4298 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4299 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4300 FF_DONTCARE, "Courier");
4301 LOGFONTA sentLogFont;
4302 CHARFORMAT2A returnedCF2A;
4304 returnedCF2A.cbSize = sizeof(returnedCF2A);
4306 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4307 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4308 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4310 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4311 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4312 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4313 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4315 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4316 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4317 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4318 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4319 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4320 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4322 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4323 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4324 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4325 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4326 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4327 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4329 /* This last test is special since we send in NULL. We clear the variables
4330 * and just compare to "System" instead of the sent in font name. */
4331 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4332 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4333 returnedCF2A.cbSize = sizeof(returnedCF2A);
4335 SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4336 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4337 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4338 ok (!strcmp("System",returnedCF2A.szFaceName),
4339 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4341 DestroyWindow(hwndRichEdit);
4345 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4346 LPBYTE pbBuff,
4347 LONG cb,
4348 LONG *pcb)
4350 const char** str = (const char**)dwCookie;
4351 int size = strlen(*str);
4352 if(size > 3) /* let's make it piecemeal for fun */
4353 size = 3;
4354 *pcb = cb;
4355 if (*pcb > size) {
4356 *pcb = size;
4358 if (*pcb > 0) {
4359 memcpy(pbBuff, *str, *pcb);
4360 *str += *pcb;
4362 return 0;
4365 static void test_EM_GETMODIFY(void)
4367 HWND hwndRichEdit = new_richedit(NULL);
4368 LRESULT result;
4369 SETTEXTEX setText;
4370 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4371 'S', 'o', 'm', 'e',
4372 'T', 'e', 'x', 't', 0};
4373 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4374 'S', 'o', 'm', 'e',
4375 'O', 't', 'h', 'e', 'r',
4376 'T', 'e', 'x', 't', 0};
4377 const char* streamText = "hello world";
4378 CHARFORMAT2A cf2;
4379 PARAFORMAT2 pf2;
4380 EDITSTREAM es;
4382 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4383 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4384 FF_DONTCARE, "Courier");
4386 setText.codepage = 1200; /* no constant for unicode */
4387 setText.flags = ST_KEEPUNDO;
4390 /* modify flag shouldn't be set when richedit is first created */
4391 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4392 ok (result == 0,
4393 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4395 /* setting modify flag should actually set it */
4396 SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4397 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4398 ok (result != 0,
4399 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4401 /* clearing modify flag should actually clear it */
4402 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4403 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4404 ok (result == 0,
4405 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4407 /* setting font doesn't change modify flag */
4408 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4409 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4410 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4411 ok (result == 0,
4412 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4414 /* setting text should set modify flag */
4415 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4416 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4417 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4418 ok (result != 0,
4419 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4421 /* undo previous text doesn't reset modify flag */
4422 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4423 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4424 ok (result != 0,
4425 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4427 /* set text with no flag to keep undo stack should not set modify flag */
4428 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4429 setText.flags = 0;
4430 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4431 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4432 ok (result == 0,
4433 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4435 /* WM_SETTEXT doesn't modify */
4436 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4437 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4438 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4439 ok (result == 0,
4440 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4442 /* clear the text */
4443 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4444 SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4445 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4446 ok (result == 0,
4447 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4449 /* replace text */
4450 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4451 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4452 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4453 SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4454 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4455 ok (result != 0,
4456 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4458 /* copy/paste text 1 */
4459 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4460 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4461 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4462 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4463 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4464 ok (result != 0,
4465 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4467 /* copy/paste text 2 */
4468 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4469 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4470 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4471 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4472 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4473 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4474 ok (result != 0,
4475 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4477 /* press char */
4478 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4479 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4480 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4481 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4482 ok (result != 0,
4483 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4485 /* press del */
4486 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4487 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4488 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4489 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4490 ok (result != 0,
4491 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4493 /* set char format */
4494 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4495 cf2.cbSize = sizeof(CHARFORMAT2A);
4496 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4497 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4498 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4499 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4500 result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4501 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4502 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4503 ok (result != 0,
4504 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4506 /* set para format */
4507 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4508 pf2.cbSize = sizeof(PARAFORMAT2);
4509 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4510 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4511 pf2.wAlignment = PFA_RIGHT;
4512 SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4513 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4514 ok (result == 0,
4515 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4517 /* EM_STREAM */
4518 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4519 es.dwCookie = (DWORD_PTR)&streamText;
4520 es.dwError = 0;
4521 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4522 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4523 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4524 ok (result != 0,
4525 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4527 DestroyWindow(hwndRichEdit);
4530 struct exsetsel_s {
4531 LONG min;
4532 LONG max;
4533 LRESULT expected_retval;
4534 int expected_getsel_start;
4535 int expected_getsel_end;
4536 BOOL todo;
4539 static const struct exsetsel_s exsetsel_tests[] = {
4540 /* sanity tests */
4541 {5, 10, 10, 5, 10 },
4542 {15, 17, 17, 15, 17 },
4543 /* test cpMax > strlen() */
4544 {0, 100, 18, 0, 18 },
4545 /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
4546 {-1, 1, 17, 17, 17 },
4547 /* test cpMin == cpMax */
4548 {5, 5, 5, 5, 5 },
4549 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4550 {-1, 0, 5, 5, 5 },
4551 {-1, 17, 5, 5, 5 },
4552 {-1, 18, 5, 5, 5 },
4553 /* test cpMin < 0 && cpMax < 0 */
4554 {-1, -1, 17, 17, 17 },
4555 {-4, -5, 17, 17, 17 },
4556 /* test cpMin >=0 && cpMax < 0 (bug 6814) */
4557 {0, -1, 18, 0, 18 },
4558 {17, -5, 18, 17, 18 },
4559 {18, -3, 17, 17, 17 },
4560 /* test if cpMin > cpMax */
4561 {15, 19, 18, 15, 18 },
4562 {19, 15, 18, 15, 18 },
4563 /* cpMin == strlen() && cpMax > cpMin */
4564 {17, 18, 18, 17, 18 },
4565 {17, 50, 18, 17, 18 },
4568 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4569 CHARRANGE cr;
4570 LRESULT result;
4571 int start, end;
4573 cr.cpMin = setsel->min;
4574 cr.cpMax = setsel->max;
4575 result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
4577 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4579 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4581 if (setsel->todo) {
4582 todo_wine {
4583 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);
4585 } else {
4586 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);
4590 static void test_EM_EXSETSEL(void)
4592 HWND hwndRichEdit = new_richedit(NULL);
4593 int i;
4594 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4596 /* sending some text to the window */
4597 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4598 /* 01234567890123456*/
4599 /* 10 */
4601 for (i = 0; i < num_tests; i++) {
4602 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4605 if (!is_lang_japanese)
4606 skip("Skip multibyte character tests on non-Japanese platform\n");
4607 else
4609 CHARRANGE cr;
4610 char bufA[MAX_BUF_LEN] = {0};
4611 LRESULT result;
4613 /* Test with multibyte character */
4614 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
4615 /* 012345 6 78901 */
4616 cr.cpMin = 4, cr.cpMax = 8;
4617 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4618 ok(result == 8, "EM_EXSETSEL return %ld expected 8\n", result);
4619 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(bufA), (LPARAM)bufA);
4620 ok(!strcmp(bufA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
4621 SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4622 ok(cr.cpMin == 4, "Selection start incorrectly: %d expected 4\n", cr.cpMin);
4623 ok(cr.cpMax == 8, "Selection end incorrectly: %d expected 8\n", cr.cpMax);
4626 DestroyWindow(hwndRichEdit);
4629 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4630 LRESULT result;
4631 int start, end;
4633 result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
4635 ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4637 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4639 if (setsel->todo) {
4640 todo_wine {
4641 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);
4643 } else {
4644 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);
4648 static void test_EM_SETSEL(void)
4650 char buffA[32] = {0};
4651 HWND hwndRichEdit = new_richedit(NULL);
4652 int i;
4653 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4655 /* sending some text to the window */
4656 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4657 /* 01234567890123456*/
4658 /* 10 */
4660 for (i = 0; i < num_tests; i++) {
4661 check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4664 SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
4665 buffA[0] = 123;
4666 SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
4667 ok(buffA[0] == 0, "selection text %s\n", buffA);
4669 if (!is_lang_japanese)
4670 skip("Skip multibyte character tests on non-Japanese platform\n");
4671 else
4673 int sel_start, sel_end;
4674 LRESULT result;
4676 /* Test with multibyte character */
4677 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
4678 /* 012345 6 78901 */
4679 result = SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
4680 ok(result == 8, "EM_SETSEL return %ld expected 8\n", result);
4681 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(buffA), (LPARAM)buffA);
4682 ok(!strcmp(buffA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
4683 result = SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4684 ok(sel_start == 4, "Selection start incorrectly: %d expected 4\n", sel_start);
4685 ok(sel_end == 8, "Selection end incorrectly: %d expected 8\n", sel_end);
4688 DestroyWindow(hwndRichEdit);
4691 static void test_EM_REPLACESEL(int redraw)
4693 HWND hwndRichEdit = new_richedit(NULL);
4694 char buffer[1024] = {0};
4695 int r;
4696 GETTEXTEX getText;
4697 CHARRANGE cr;
4698 CHAR rtfstream[] = "{\\rtf1 TestSomeText}";
4699 CHAR urtfstream[] = "{\\urtf1 TestSomeText}";
4701 /* sending some text to the window */
4702 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4703 /* 01234567890123456*/
4704 /* 10 */
4706 /* FIXME add more tests */
4707 SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
4708 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
4709 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4710 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4711 r = strcmp(buffer, "testing");
4712 ok(0 == r, "expected %d, got %d\n", 0, r);
4714 DestroyWindow(hwndRichEdit);
4716 hwndRichEdit = new_richedit(NULL);
4718 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4719 SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4721 /* Test behavior with carriage returns and newlines */
4722 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4723 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
4724 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4725 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4726 r = strcmp(buffer, "RichEdit1");
4727 ok(0 == r, "expected %d, got %d\n", 0, r);
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, "RichEdit1") == 0,
4735 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\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)"RichEdit1\r");
4743 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4744 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4745 r = strcmp(buffer, "RichEdit1\r\n");
4746 ok(0 == r, "expected %d, got %d\n", 0, r);
4747 getText.cb = 1024;
4748 getText.codepage = CP_ACP;
4749 getText.flags = GT_DEFAULT;
4750 getText.lpDefaultChar = NULL;
4751 getText.lpUsedDefChar = NULL;
4752 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4753 ok(strcmp(buffer, "RichEdit1\r") == 0,
4754 "EM_GETTEXTEX returned incorrect string\n");
4756 /* Test number of lines reported after EM_REPLACESEL */
4757 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4758 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4760 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4761 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
4762 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4764 /* Test number of lines reported after EM_REPLACESEL */
4765 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4766 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4768 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4769 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4770 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4771 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4773 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4774 r = strcmp(buffer, "RichEdit1\r\n");
4775 ok(0 == r, "expected %d, got %d\n", 0, r);
4776 getText.cb = 1024;
4777 getText.codepage = CP_ACP;
4778 getText.flags = GT_DEFAULT;
4779 getText.lpDefaultChar = NULL;
4780 getText.lpUsedDefChar = NULL;
4781 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4782 ok(strcmp(buffer, "RichEdit1\r") == 0,
4783 "EM_GETTEXTEX returned incorrect string\n");
4785 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4786 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4787 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4788 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4790 /* The following tests show that richedit should handle the special \r\r\n
4791 sequence by turning it into a single space on insertion. However,
4792 EM_REPLACESEL on WinXP returns the number of characters in the original
4793 string.
4796 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4797 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
4798 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4799 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4800 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4801 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4802 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4804 /* Test the actual string */
4805 getText.cb = 1024;
4806 getText.codepage = CP_ACP;
4807 getText.flags = GT_DEFAULT;
4808 getText.lpDefaultChar = NULL;
4809 getText.lpUsedDefChar = NULL;
4810 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4811 ok(strcmp(buffer, "\r\r") == 0,
4812 "EM_GETTEXTEX returned incorrect string\n");
4814 /* Test number of lines reported after EM_REPLACESEL */
4815 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4816 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4818 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4819 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
4820 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4821 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4822 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4823 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4824 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4826 /* Test the actual string */
4827 getText.cb = 1024;
4828 getText.codepage = CP_ACP;
4829 getText.flags = GT_DEFAULT;
4830 getText.lpDefaultChar = NULL;
4831 getText.lpUsedDefChar = NULL;
4832 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4833 ok(strcmp(buffer, " ") == 0,
4834 "EM_GETTEXTEX returned incorrect string\n");
4836 /* Test number of lines reported after EM_REPLACESEL */
4837 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4838 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4840 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4841 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
4842 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4843 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4844 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4845 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4846 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4848 /* Test the actual string */
4849 getText.cb = 1024;
4850 getText.codepage = CP_ACP;
4851 getText.flags = GT_DEFAULT;
4852 getText.lpDefaultChar = NULL;
4853 getText.lpUsedDefChar = NULL;
4854 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4855 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4856 "EM_GETTEXTEX returned incorrect string\n");
4858 /* Test number of lines reported after EM_REPLACESEL */
4859 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4860 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4862 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4863 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
4864 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4865 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4866 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4867 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4868 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4870 /* Test the actual string */
4871 getText.cb = 1024;
4872 getText.codepage = CP_ACP;
4873 getText.flags = GT_DEFAULT;
4874 getText.lpDefaultChar = NULL;
4875 getText.lpUsedDefChar = NULL;
4876 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4877 ok(strcmp(buffer, " \r") == 0,
4878 "EM_GETTEXTEX returned incorrect string\n");
4880 /* Test number of lines reported after EM_REPLACESEL */
4881 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4882 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4884 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4885 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
4886 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4887 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4888 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4889 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4890 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4892 /* Test the actual string */
4893 getText.cb = 1024;
4894 getText.codepage = CP_ACP;
4895 getText.flags = GT_DEFAULT;
4896 getText.lpDefaultChar = NULL;
4897 getText.lpUsedDefChar = NULL;
4898 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4899 ok(strcmp(buffer, " \r\r") == 0,
4900 "EM_GETTEXTEX returned incorrect string\n");
4902 /* Test number of lines reported after EM_REPLACESEL */
4903 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4904 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4906 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4907 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
4908 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4909 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4910 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4911 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4912 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4914 /* Test the actual string */
4915 getText.cb = 1024;
4916 getText.codepage = CP_ACP;
4917 getText.flags = GT_DEFAULT;
4918 getText.lpDefaultChar = NULL;
4919 getText.lpUsedDefChar = NULL;
4920 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4921 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4922 "EM_GETTEXTEX returned incorrect string\n");
4924 /* Test number of lines reported after EM_REPLACESEL */
4925 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4926 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4928 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4929 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
4930 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4931 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4932 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4933 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4934 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4936 /* Test the actual string */
4937 getText.cb = 1024;
4938 getText.codepage = CP_ACP;
4939 getText.flags = GT_DEFAULT;
4940 getText.lpDefaultChar = NULL;
4941 getText.lpUsedDefChar = NULL;
4942 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4943 ok(strcmp(buffer, "\r\r") == 0,
4944 "EM_GETTEXTEX returned incorrect string\n");
4946 /* Test number of lines reported after EM_REPLACESEL */
4947 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4948 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4950 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4951 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
4952 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4953 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4954 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4955 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4956 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4958 /* Test the actual string */
4959 getText.cb = 1024;
4960 getText.codepage = CP_ACP;
4961 getText.flags = GT_DEFAULT;
4962 getText.lpDefaultChar = NULL;
4963 getText.lpUsedDefChar = NULL;
4964 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4965 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4966 "EM_GETTEXTEX returned incorrect string\n");
4968 /* Test number of lines reported after EM_REPLACESEL */
4969 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4970 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4972 /* Test with multibyte character */
4973 if (!is_lang_japanese)
4974 skip("Skip multibyte character tests on non-Japanese platform\n");
4975 else
4977 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4978 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"abc\x8e\xf0");
4979 todo_wine ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4980 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4981 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
4982 ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
4983 ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
4984 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4985 ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
4986 ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
4988 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4989 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"{\\rtf abc\x8e\xf0}");
4990 todo_wine ok(r == 4, "EM_REPLACESEL returned %d, expected 4\n", r);
4991 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4992 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
4993 todo_wine ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
4994 todo_wine ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
4995 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4996 todo_wine ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
4997 todo_wine ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5000 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5001 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5002 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5003 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5004 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5005 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5006 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5007 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5008 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5010 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5011 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urtfstream);
5012 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5013 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5014 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5015 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5016 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5017 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5018 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5020 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Wine");
5021 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5022 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5023 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5024 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5025 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5026 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5027 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5028 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5029 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5031 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 Wine}");
5032 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5033 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5034 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5035 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5036 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5037 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5038 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5039 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5040 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5042 if (!redraw)
5043 /* This is needed to avoid interferring with keybd_event calls
5044 * on other tests that simulate keyboard events. */
5045 SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
5047 DestroyWindow(hwndRichEdit);
5050 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
5051 * to test the state of the modifiers (Ctrl/Alt/Shift).
5053 * Therefore Ctrl-<key> keystrokes need to be simulated with
5054 * keybd_event or by using SetKeyboardState to set the modifiers
5055 * and SendMessage to simulate the keystrokes.
5057 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
5059 LRESULT result;
5060 hold_key(VK_CONTROL);
5061 result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
5062 release_key(VK_CONTROL);
5063 return result;
5066 static void test_WM_PASTE(void)
5068 int result;
5069 char buffer[1024] = {0};
5070 const char* text1 = "testing paste\r";
5071 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
5072 const char* text1_after = "testing paste\r\n";
5073 const char* text2 = "testing paste\r\rtesting paste";
5074 const char* text2_after = "testing paste\r\n\r\ntesting paste";
5075 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
5076 HWND hwndRichEdit = new_richedit(NULL);
5078 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5079 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
5081 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
5082 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5083 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
5084 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5085 /* Pasted text should be visible at this step */
5086 result = strcmp(text1_step1, buffer);
5087 ok(result == 0,
5088 "test paste: strcmp = %i, text='%s'\n", result, buffer);
5090 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
5091 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5092 /* Text should be the same as before (except for \r -> \r\n conversion) */
5093 result = strcmp(text1_after, buffer);
5094 ok(result == 0,
5095 "test paste: strcmp = %i, text='%s'\n", result, buffer);
5097 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
5098 SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
5099 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
5100 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5101 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
5102 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5103 /* Pasted text should be visible at this step */
5104 result = strcmp(text3, buffer);
5105 ok(result == 0,
5106 "test paste: strcmp = %i\n", result);
5107 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
5108 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5109 /* Text should be the same as before (except for \r -> \r\n conversion) */
5110 result = strcmp(text2_after, buffer);
5111 ok(result == 0,
5112 "test paste: strcmp = %i\n", result);
5113 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
5114 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5115 /* Text should revert to post-paste state */
5116 result = strcmp(buffer,text3);
5117 ok(result == 0,
5118 "test paste: strcmp = %i\n", result);
5120 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5121 /* Send WM_CHAR to simulate Ctrl-V */
5122 SendMessageA(hwndRichEdit, WM_CHAR, 22,
5123 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5124 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5125 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
5126 result = strcmp(buffer,"");
5127 ok(result == 0,
5128 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5130 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
5131 * with SetKeyboard state. */
5133 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5134 /* Simulates paste (Ctrl-V) */
5135 hold_key(VK_CONTROL);
5136 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
5137 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5138 release_key(VK_CONTROL);
5139 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5140 result = strcmp(buffer,"paste");
5141 ok(result == 0,
5142 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5144 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5145 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
5146 /* Simulates copy (Ctrl-C) */
5147 hold_key(VK_CONTROL);
5148 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
5149 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
5150 release_key(VK_CONTROL);
5151 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5152 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5153 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5154 result = strcmp(buffer,"testing");
5155 ok(result == 0,
5156 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5158 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
5159 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
5160 /* Simulates select all (Ctrl-A) */
5161 hold_key(VK_CONTROL);
5162 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
5163 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
5164 /* Simulates select cut (Ctrl-X) */
5165 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
5166 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
5167 release_key(VK_CONTROL);
5168 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5169 result = strcmp(buffer,"");
5170 ok(result == 0,
5171 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5172 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5173 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5174 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5175 result = strcmp(buffer,"cut\r\n");
5176 ok(result == 0,
5177 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5178 /* Simulates undo (Ctrl-Z) */
5179 hold_key(VK_CONTROL);
5180 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
5181 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
5182 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5183 result = strcmp(buffer,"");
5184 ok(result == 0,
5185 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5186 /* Simulates redo (Ctrl-Y) */
5187 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
5188 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
5189 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5190 result = strcmp(buffer,"cut\r\n");
5191 ok(result == 0,
5192 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5193 release_key(VK_CONTROL);
5195 DestroyWindow(hwndRichEdit);
5198 static void test_EM_FORMATRANGE(void)
5200 int r, i, tpp_x, tpp_y;
5201 HDC hdc;
5202 HWND hwndRichEdit = new_richedit(NULL);
5203 FORMATRANGE fr;
5204 BOOL skip_non_english;
5205 static const struct {
5206 const char *string; /* The string */
5207 int first; /* First 'pagebreak', 0 for don't care */
5208 int second; /* Second 'pagebreak', 0 for don't care */
5209 } fmtstrings[] = {
5210 {"WINE wine", 0, 0},
5211 {"WINE wineWine", 0, 0},
5212 {"WINE\r\nwine\r\nwine", 5, 10},
5213 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
5214 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
5217 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
5218 if (skip_non_english)
5219 skip("Skipping some tests on non-English platform\n");
5221 hdc = GetDC(hwndRichEdit);
5222 ok(hdc != NULL, "Could not get HDC\n");
5224 /* Calculate the twips per pixel */
5225 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
5226 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
5228 /* Test the simple case where all the text fits in the page rect. */
5229 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
5230 fr.hdc = fr.hdcTarget = hdc;
5231 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5232 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
5233 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
5234 fr.chrg.cpMin = 0;
5235 fr.chrg.cpMax = -1;
5236 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5237 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
5239 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
5240 fr.rc.bottom = fr.rcPage.bottom;
5241 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5242 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
5244 SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
5246 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
5248 GETTEXTLENGTHEX gtl;
5249 SIZE stringsize;
5250 int len;
5252 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
5254 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5255 gtl.codepage = CP_ACP;
5256 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5258 /* Get some size information for the string */
5259 GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
5261 /* Define the box to be half the width needed and a bit larger than the height.
5262 * Changes to the width means we have at least 2 pages. Changes to the height
5263 * is done so we can check the changing of fr.rc.bottom.
5265 fr.hdc = fr.hdcTarget = hdc;
5266 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5267 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
5268 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
5270 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5271 todo_wine {
5272 ok(r == len, "Expected %d, got %d\n", len, r);
5275 /* We know that the page can't hold the full string. See how many characters
5276 * are on the first one
5278 fr.chrg.cpMin = 0;
5279 fr.chrg.cpMax = -1;
5280 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5281 todo_wine {
5282 if (! skip_non_english)
5283 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
5285 if (fmtstrings[i].first)
5286 todo_wine {
5287 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
5289 else
5290 ok(r < len, "Expected < %d, got %d\n", len, r);
5292 /* Do another page */
5293 fr.chrg.cpMin = r;
5294 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5295 if (fmtstrings[i].second)
5296 todo_wine {
5297 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
5299 else if (! skip_non_english)
5300 ok (r < len, "Expected < %d, got %d\n", len, r);
5302 /* There is at least on more page, but we don't care */
5304 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5305 todo_wine {
5306 ok(r == len, "Expected %d, got %d\n", len, r);
5310 ReleaseDC(NULL, hdc);
5311 DestroyWindow(hwndRichEdit);
5314 static int nCallbackCount = 0;
5316 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5317 LONG cb, LONG* pcb)
5319 const char text[] = {'t','e','s','t'};
5321 if (sizeof(text) <= cb)
5323 if ((int)dwCookie != nCallbackCount)
5325 *pcb = 0;
5326 return 0;
5329 memcpy (pbBuff, text, sizeof(text));
5330 *pcb = sizeof(text);
5332 nCallbackCount++;
5334 return 0;
5336 else
5337 return 1; /* indicates callback failed */
5340 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5341 LPBYTE pbBuff,
5342 LONG cb,
5343 LONG *pcb)
5345 const char** str = (const char**)dwCookie;
5346 int size = strlen(*str);
5347 *pcb = cb;
5348 if (*pcb > size) {
5349 *pcb = size;
5351 if (*pcb > 0) {
5352 memcpy(pbBuff, *str, *pcb);
5353 *str += *pcb;
5355 return 0;
5358 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5359 LPBYTE pbBuff,
5360 LONG cb,
5361 LONG *pcb)
5363 DWORD *phase = (DWORD *)dwCookie;
5365 if(*phase == 0){
5366 static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5367 *pcb = sizeof(first) - 1;
5368 memcpy(pbBuff, first, *pcb);
5369 }else if(*phase == 1){
5370 static const char second[] = "\x8f\xc3\x8b";
5371 *pcb = sizeof(second) - 1;
5372 memcpy(pbBuff, second, *pcb);
5373 }else
5374 *pcb = 0;
5376 ++*phase;
5378 return 0;
5381 struct StringWithLength {
5382 int length;
5383 char *buffer;
5386 /* This callback is used to handled the null characters in a string. */
5387 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5388 LPBYTE pbBuff,
5389 LONG cb,
5390 LONG *pcb)
5392 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5393 int size = str->length;
5394 *pcb = cb;
5395 if (*pcb > size) {
5396 *pcb = size;
5398 if (*pcb > 0) {
5399 memcpy(pbBuff, str->buffer, *pcb);
5400 str->buffer += *pcb;
5401 str->length -= *pcb;
5403 return 0;
5406 static void test_EM_STREAMIN(void)
5408 HWND hwndRichEdit = new_richedit(NULL);
5409 DWORD phase;
5410 LRESULT result;
5411 EDITSTREAM es;
5412 char buffer[1024] = {0}, tmp[16];
5413 CHARRANGE range;
5415 const char * streamText0 = "{\\rtf1 TestSomeText}";
5416 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5417 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5418 const char * ptr;
5420 const char * streamText1 =
5421 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5422 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5423 "}\r\n";
5425 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5426 const char * streamText2 =
5427 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5428 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5429 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5430 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5431 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5432 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5433 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5435 const char * streamText3 = "RichEdit1";
5437 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5439 const char * streamText4 =
5440 "This text just needs to be long enough to cause run to be split onto "
5441 "two separate lines and make sure the null terminating character is "
5442 "handled properly.\0";
5444 const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5446 int length4 = strlen(streamText4) + 1;
5447 struct StringWithLength cookieForStream4 = {
5448 length4,
5449 (char *)streamText4,
5452 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5453 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5454 struct StringWithLength cookieForStream5 = {
5455 sizeof(streamText5),
5456 (char *)streamText5,
5459 /* Minimal test without \par at the end */
5460 es.dwCookie = (DWORD_PTR)&streamText0;
5461 es.dwError = 0;
5462 es.pfnCallback = test_EM_STREAMIN_esCallback;
5463 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5464 ok(result == 12, "got %ld, expected %d\n", result, 12);
5466 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5467 ok (result == 12,
5468 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5469 result = strcmp (buffer,"TestSomeText");
5470 ok (result == 0,
5471 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5472 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5474 /* Native richedit 2.0 ignores last \par */
5475 ptr = streamText0a;
5476 es.dwCookie = (DWORD_PTR)&ptr;
5477 es.dwError = 0;
5478 es.pfnCallback = test_EM_STREAMIN_esCallback;
5479 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5480 ok(result == 12, "got %ld, expected %d\n", result, 12);
5482 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5483 ok (result == 12,
5484 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5485 result = strcmp (buffer,"TestSomeText");
5486 ok (result == 0,
5487 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5488 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5490 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5491 es.dwCookie = (DWORD_PTR)&streamText0b;
5492 es.dwError = 0;
5493 es.pfnCallback = test_EM_STREAMIN_esCallback;
5494 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5495 ok(result == 13, "got %ld, expected %d\n", result, 13);
5497 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5498 ok (result == 14,
5499 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5500 result = strcmp (buffer,"TestSomeText\r\n");
5501 ok (result == 0,
5502 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5503 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5505 /* Show that when using SFF_SELECTION the last \par is not ignored. */
5506 ptr = streamText0a;
5507 es.dwCookie = (DWORD_PTR)&ptr;
5508 es.dwError = 0;
5509 es.pfnCallback = test_EM_STREAMIN_esCallback;
5510 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5511 ok(result == 12, "got %ld, expected %d\n", result, 12);
5513 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5514 ok (result == 12,
5515 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5516 result = strcmp (buffer,"TestSomeText");
5517 ok (result == 0,
5518 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5519 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5521 range.cpMin = 0;
5522 range.cpMax = -1;
5523 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5524 ok (result == 13, "got %ld\n", result);
5526 ptr = streamText0a;
5527 es.dwCookie = (DWORD_PTR)&ptr;
5528 es.dwError = 0;
5529 es.pfnCallback = test_EM_STREAMIN_esCallback;
5531 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5532 ok(result == 13, "got %ld, expected 13\n", result);
5534 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5535 ok (result == 14,
5536 "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5537 result = strcmp (buffer,"TestSomeText\r\n");
5538 ok (result == 0,
5539 "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5540 ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5542 es.dwCookie = (DWORD_PTR)&streamText1;
5543 es.dwError = 0;
5544 es.pfnCallback = test_EM_STREAMIN_esCallback;
5545 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5546 ok(result == 12, "got %ld, expected %d\n", result, 12);
5548 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5549 ok (result == 12,
5550 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5551 result = strcmp (buffer,"TestSomeText");
5552 ok (result == 0,
5553 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5554 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5556 es.dwCookie = (DWORD_PTR)&streamText2;
5557 es.dwError = 0;
5558 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5559 ok(result == 0, "got %ld, expected %d\n", result, 0);
5561 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5562 ok (result == 0,
5563 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5564 ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5565 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5567 es.dwCookie = (DWORD_PTR)&streamText3;
5568 es.dwError = 0;
5569 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5570 ok(result == 0, "got %ld, expected %d\n", result, 0);
5572 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5573 ok (result == 0,
5574 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5575 ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5576 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5578 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5579 es.dwError = 0;
5580 es.pfnCallback = test_EM_STREAMIN_esCallback;
5581 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5582 ok(result == 18, "got %ld, expected %d\n", result, 18);
5584 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5585 ok(result == 15,
5586 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5587 result = strcmp (buffer,"TestUTF8WithBOM");
5588 ok(result == 0,
5589 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5590 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5592 phase = 0;
5593 es.dwCookie = (DWORD_PTR)&phase;
5594 es.dwError = 0;
5595 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5596 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5597 ok(result == 8, "got %ld\n", result);
5599 WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
5601 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5602 ok(result == 3,
5603 "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
5604 result = memcmp (buffer, tmp, 3);
5605 ok(result == 0,
5606 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
5607 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
5609 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5610 es.dwError = 0;
5611 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5612 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5613 ok(result == length4, "got %ld, expected %d\n", result, length4);
5615 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5616 ok (result == length4,
5617 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5618 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5620 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5621 es.dwError = 0;
5622 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5623 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5624 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5626 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5627 ok (result == length5,
5628 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5629 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5631 DestroyWindow(hwndRichEdit);
5634 static void test_EM_StreamIn_Undo(void)
5636 /* The purpose of this test is to determine when a EM_StreamIn should be
5637 * undoable. This is important because WM_PASTE currently uses StreamIn and
5638 * pasting should always be undoable but streaming isn't always.
5640 * cases to test:
5641 * StreamIn plain text without SFF_SELECTION.
5642 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5643 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5644 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5645 * Feel free to add tests for other text modes or StreamIn things.
5649 HWND hwndRichEdit = new_richedit(NULL);
5650 LRESULT result;
5651 EDITSTREAM es;
5652 char buffer[1024] = {0};
5653 const char randomtext[] = "Some text";
5655 es.pfnCallback = EditStreamCallback;
5657 /* StreamIn, no SFF_SELECTION */
5658 es.dwCookie = nCallbackCount;
5659 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5660 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5661 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5662 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5663 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5664 result = strcmp (buffer,"test");
5665 ok (result == 0,
5666 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5668 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5669 ok (result == FALSE,
5670 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5672 /* StreamIn, SFF_SELECTION, but nothing selected */
5673 es.dwCookie = nCallbackCount;
5674 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5675 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5676 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5677 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5678 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5679 result = strcmp (buffer,"testSome text");
5680 ok (result == 0,
5681 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5683 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5684 ok (result == TRUE,
5685 "EM_STREAMIN with SFF_SELECTION but no selection set "
5686 "should create an undo\n");
5688 /* StreamIn, SFF_SELECTION, with a selection */
5689 es.dwCookie = nCallbackCount;
5690 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5691 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5692 SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
5693 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5694 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5695 result = strcmp (buffer,"Sometesttext");
5696 ok (result == 0,
5697 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5699 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5700 ok (result == TRUE,
5701 "EM_STREAMIN with SFF_SELECTION and selection set "
5702 "should create an undo\n");
5704 DestroyWindow(hwndRichEdit);
5707 static BOOL is_em_settextex_supported(HWND hwnd)
5709 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5710 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5713 static void test_unicode_conversions(void)
5715 static const WCHAR tW[] = {'t',0};
5716 static const WCHAR teW[] = {'t','e',0};
5717 static const WCHAR textW[] = {'t','e','s','t',0};
5718 static const char textA[] = "test";
5719 char bufA[64];
5720 WCHAR bufW[64];
5721 HWND hwnd;
5722 int em_settextex_supported, ret;
5724 #define set_textA(hwnd, wm_set_text, txt) \
5725 do { \
5726 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5727 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5728 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5729 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5730 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5731 } while(0)
5732 #define expect_textA(hwnd, wm_get_text, txt) \
5733 do { \
5734 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5735 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5736 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5737 memset(bufA, 0xAA, sizeof(bufA)); \
5738 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5739 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5740 ret = lstrcmpA(bufA, txt); \
5741 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5742 } while(0)
5744 #define set_textW(hwnd, wm_set_text, txt) \
5745 do { \
5746 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5747 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5748 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5749 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5750 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5751 } while(0)
5752 #define expect_textW(hwnd, wm_get_text, txt) \
5753 do { \
5754 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5755 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5756 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5757 memset(bufW, 0xAA, sizeof(bufW)); \
5758 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5759 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5760 ret = lstrcmpW(bufW, txt); \
5761 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5762 } while(0)
5763 #define expect_empty(hwnd, wm_get_text) \
5764 do { \
5765 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5766 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5767 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5768 memset(bufA, 0xAA, sizeof(bufA)); \
5769 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5770 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5771 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5772 } while(0)
5774 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5775 0, 0, 200, 60, 0, 0, 0, 0);
5776 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5778 ret = IsWindowUnicode(hwnd);
5779 ok(ret, "RichEdit20W should be unicode under NT\n");
5781 /* EM_SETTEXTEX is supported starting from version 3.0 */
5782 em_settextex_supported = is_em_settextex_supported(hwnd);
5783 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5784 em_settextex_supported ? "" : "NOT ");
5786 expect_empty(hwnd, WM_GETTEXT);
5787 expect_empty(hwnd, EM_GETTEXTEX);
5789 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5790 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5791 expect_textA(hwnd, WM_GETTEXT, "t");
5792 expect_textA(hwnd, EM_GETTEXTEX, "t");
5793 expect_textW(hwnd, EM_GETTEXTEX, tW);
5795 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5796 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5797 expect_textA(hwnd, WM_GETTEXT, "te");
5798 expect_textA(hwnd, EM_GETTEXTEX, "te");
5799 expect_textW(hwnd, EM_GETTEXTEX, teW);
5801 set_textA(hwnd, WM_SETTEXT, NULL);
5802 expect_empty(hwnd, WM_GETTEXT);
5803 expect_empty(hwnd, EM_GETTEXTEX);
5805 set_textA(hwnd, WM_SETTEXT, textA);
5806 expect_textA(hwnd, WM_GETTEXT, textA);
5807 expect_textA(hwnd, EM_GETTEXTEX, textA);
5808 expect_textW(hwnd, EM_GETTEXTEX, textW);
5810 if (em_settextex_supported)
5812 set_textA(hwnd, EM_SETTEXTEX, textA);
5813 expect_textA(hwnd, WM_GETTEXT, textA);
5814 expect_textA(hwnd, EM_GETTEXTEX, textA);
5815 expect_textW(hwnd, EM_GETTEXTEX, textW);
5818 set_textW(hwnd, WM_SETTEXT, textW);
5819 expect_textW(hwnd, WM_GETTEXT, textW);
5820 expect_textA(hwnd, WM_GETTEXT, textA);
5821 expect_textW(hwnd, EM_GETTEXTEX, textW);
5822 expect_textA(hwnd, EM_GETTEXTEX, textA);
5824 if (em_settextex_supported)
5826 set_textW(hwnd, EM_SETTEXTEX, textW);
5827 expect_textW(hwnd, WM_GETTEXT, textW);
5828 expect_textA(hwnd, WM_GETTEXT, textA);
5829 expect_textW(hwnd, EM_GETTEXTEX, textW);
5830 expect_textA(hwnd, EM_GETTEXTEX, textA);
5832 DestroyWindow(hwnd);
5834 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5835 0, 0, 200, 60, 0, 0, 0, 0);
5836 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5838 ret = IsWindowUnicode(hwnd);
5839 ok(!ret, "RichEdit20A should NOT be unicode\n");
5841 set_textA(hwnd, WM_SETTEXT, textA);
5842 expect_textA(hwnd, WM_GETTEXT, textA);
5843 expect_textA(hwnd, EM_GETTEXTEX, textA);
5844 expect_textW(hwnd, EM_GETTEXTEX, textW);
5846 if (em_settextex_supported)
5848 set_textA(hwnd, EM_SETTEXTEX, textA);
5849 expect_textA(hwnd, WM_GETTEXT, textA);
5850 expect_textA(hwnd, EM_GETTEXTEX, textA);
5851 expect_textW(hwnd, EM_GETTEXTEX, textW);
5854 set_textW(hwnd, WM_SETTEXT, textW);
5855 expect_textW(hwnd, WM_GETTEXT, textW);
5856 expect_textA(hwnd, WM_GETTEXT, textA);
5857 expect_textW(hwnd, EM_GETTEXTEX, textW);
5858 expect_textA(hwnd, EM_GETTEXTEX, textA);
5860 if (em_settextex_supported)
5862 set_textW(hwnd, EM_SETTEXTEX, textW);
5863 expect_textW(hwnd, WM_GETTEXT, textW);
5864 expect_textA(hwnd, WM_GETTEXT, textA);
5865 expect_textW(hwnd, EM_GETTEXTEX, textW);
5866 expect_textA(hwnd, EM_GETTEXTEX, textA);
5868 DestroyWindow(hwnd);
5871 static void test_WM_CHAR(void)
5873 HWND hwnd;
5874 int ret;
5875 const char * char_list = "abc\rabc\r";
5876 const char * expected_content_single = "abcabc";
5877 const char * expected_content_multi = "abc\r\nabc\r\n";
5878 char buffer[64] = {0};
5879 const char * p;
5881 /* single-line control must IGNORE carriage returns */
5882 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5883 0, 0, 200, 60, 0, 0, 0, 0);
5884 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5886 p = char_list;
5887 while (*p != '\0') {
5888 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5889 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5890 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5891 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5892 p++;
5895 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5896 ret = strcmp(buffer, expected_content_single);
5897 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5899 DestroyWindow(hwnd);
5901 /* multi-line control inserts CR normally */
5902 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5903 0, 0, 200, 60, 0, 0, 0, 0);
5904 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5906 p = char_list;
5907 while (*p != '\0') {
5908 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5909 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5910 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5911 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5912 p++;
5915 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5916 ret = strcmp(buffer, expected_content_multi);
5917 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5919 DestroyWindow(hwnd);
5922 static void test_EM_GETTEXTLENGTHEX(void)
5924 HWND hwnd;
5925 GETTEXTLENGTHEX gtl;
5926 int ret;
5927 const char * base_string = "base string";
5928 const char * test_string = "a\nb\n\n\r\n";
5929 const char * test_string_after = "a";
5930 const char * test_string_2 = "a\rtest\rstring";
5931 char buffer[64] = {0};
5933 /* single line */
5934 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5935 0, 0, 200, 60, 0, 0, 0, 0);
5936 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5938 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5939 gtl.codepage = CP_ACP;
5940 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5941 ok(ret == 0, "ret %d\n",ret);
5943 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5944 gtl.codepage = CP_ACP;
5945 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5946 ok(ret == 0, "ret %d\n",ret);
5948 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5950 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5951 gtl.codepage = CP_ACP;
5952 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5953 ok(ret == strlen(base_string), "ret %d\n",ret);
5955 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5956 gtl.codepage = CP_ACP;
5957 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5958 ok(ret == strlen(base_string), "ret %d\n",ret);
5960 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5962 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5963 gtl.codepage = CP_ACP;
5964 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5965 ok(ret == 1, "ret %d\n",ret);
5967 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5968 gtl.codepage = CP_ACP;
5969 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5970 ok(ret == 1, "ret %d\n",ret);
5972 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5973 ret = strcmp(buffer, test_string_after);
5974 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5976 DestroyWindow(hwnd);
5978 /* multi line */
5979 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5980 0, 0, 200, 60, 0, 0, 0, 0);
5981 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5983 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5984 gtl.codepage = CP_ACP;
5985 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5986 ok(ret == 0, "ret %d\n",ret);
5988 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5989 gtl.codepage = CP_ACP;
5990 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5991 ok(ret == 0, "ret %d\n",ret);
5993 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5995 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5996 gtl.codepage = CP_ACP;
5997 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5998 ok(ret == strlen(base_string), "ret %d\n",ret);
6000 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6001 gtl.codepage = CP_ACP;
6002 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6003 ok(ret == strlen(base_string), "ret %d\n",ret);
6005 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6007 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6008 gtl.codepage = CP_ACP;
6009 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6010 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
6012 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6013 gtl.codepage = CP_ACP;
6014 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6015 ok(ret == strlen(test_string_2), "ret %d\n",ret);
6017 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6019 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6020 gtl.codepage = CP_ACP;
6021 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6022 ok(ret == 10, "ret %d\n",ret);
6024 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6025 gtl.codepage = CP_ACP;
6026 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6027 ok(ret == 6, "ret %d\n",ret);
6029 /* Unicode/NUMCHARS/NUMBYTES */
6030 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6032 gtl.flags = GTL_DEFAULT;
6033 gtl.codepage = 1200;
6034 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6035 ok(ret == lstrlenA(test_string_2),
6036 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6038 gtl.flags = GTL_NUMCHARS;
6039 gtl.codepage = 1200;
6040 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6041 ok(ret == lstrlenA(test_string_2),
6042 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6044 gtl.flags = GTL_NUMBYTES;
6045 gtl.codepage = 1200;
6046 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6047 ok(ret == lstrlenA(test_string_2)*2,
6048 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6050 gtl.flags = GTL_PRECISE;
6051 gtl.codepage = 1200;
6052 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6053 ok(ret == lstrlenA(test_string_2)*2,
6054 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6056 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6057 gtl.codepage = 1200;
6058 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6059 ok(ret == lstrlenA(test_string_2),
6060 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6062 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
6063 gtl.codepage = 1200;
6064 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6065 ok(ret == E_INVALIDARG,
6066 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
6068 DestroyWindow(hwnd);
6072 /* globals that parent and child access when checking event masks & notifications */
6073 static HWND eventMaskEditHwnd = 0;
6074 static int queriedEventMask;
6075 static int watchForEventMask = 0;
6077 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
6078 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6080 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
6082 queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6084 return DefWindowProcA(hwnd, message, wParam, lParam);
6087 /* test event masks in combination with WM_COMMAND */
6088 static void test_eventMask(void)
6090 HWND parent;
6091 int ret, style;
6092 WNDCLASSA cls;
6093 const char text[] = "foo bar\n";
6094 int eventMask;
6096 /* register class to capture WM_COMMAND */
6097 cls.style = 0;
6098 cls.lpfnWndProc = ParentMsgCheckProcA;
6099 cls.cbClsExtra = 0;
6100 cls.cbWndExtra = 0;
6101 cls.hInstance = GetModuleHandleA(0);
6102 cls.hIcon = 0;
6103 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6104 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6105 cls.lpszMenuName = NULL;
6106 cls.lpszClassName = "EventMaskParentClass";
6107 if(!RegisterClassA(&cls)) assert(0);
6109 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6110 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6111 ok (parent != 0, "Failed to create parent window\n");
6113 eventMaskEditHwnd = new_richedit(parent);
6114 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
6116 eventMask = ENM_CHANGE | ENM_UPDATE;
6117 ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
6118 ok(ret == ENM_NONE, "wrong event mask\n");
6119 ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6120 ok(ret == eventMask, "failed to set event mask\n");
6122 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
6123 queriedEventMask = 0; /* initialize to something other than we expect */
6124 watchForEventMask = EN_CHANGE;
6125 ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
6126 ok(ret == TRUE, "failed to set text\n");
6127 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
6128 notification in response to WM_SETTEXT */
6129 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6130 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6132 /* check to see if EN_CHANGE is sent when redraw is turned off */
6133 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6134 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6135 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
6136 /* redraw is disabled by making the window invisible. */
6137 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6138 queriedEventMask = 0; /* initialize to something other than we expect */
6139 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6140 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6141 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6142 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
6143 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6145 /* check to see if EN_UPDATE is sent when the editor isn't visible */
6146 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6147 style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
6148 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
6149 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6150 watchForEventMask = EN_UPDATE;
6151 queriedEventMask = 0; /* initialize to something other than we expect */
6152 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6153 ok(queriedEventMask == 0,
6154 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6155 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
6156 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6157 queriedEventMask = 0; /* initialize to something other than we expect */
6158 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6159 ok(queriedEventMask == eventMask,
6160 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6163 DestroyWindow(parent);
6166 static int received_WM_NOTIFY = 0;
6167 static int modify_at_WM_NOTIFY = 0;
6168 static BOOL filter_on_WM_NOTIFY = FALSE;
6169 static HWND hwndRichedit_WM_NOTIFY;
6171 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6173 if(message == WM_NOTIFY)
6175 received_WM_NOTIFY = 1;
6176 modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
6177 if (filter_on_WM_NOTIFY) return TRUE;
6179 return DefWindowProcA(hwnd, message, wParam, lParam);
6182 static void test_WM_NOTIFY(void)
6184 HWND parent;
6185 WNDCLASSA cls;
6186 CHARFORMAT2A cf2;
6187 int sel_start, sel_end;
6189 /* register class to capture WM_NOTIFY */
6190 cls.style = 0;
6191 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
6192 cls.cbClsExtra = 0;
6193 cls.cbWndExtra = 0;
6194 cls.hInstance = GetModuleHandleA(0);
6195 cls.hIcon = 0;
6196 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6197 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6198 cls.lpszMenuName = NULL;
6199 cls.lpszClassName = "WM_NOTIFY_ParentClass";
6200 if(!RegisterClassA(&cls)) assert(0);
6202 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6203 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6204 ok (parent != 0, "Failed to create parent window\n");
6206 hwndRichedit_WM_NOTIFY = new_richedit(parent);
6207 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
6209 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
6211 /* Notifications for selection change should only be sent when selection
6212 actually changes. EM_SETCHARFORMAT is one message that calls
6213 ME_CommitUndo, which should check whether message should be sent */
6214 received_WM_NOTIFY = 0;
6215 cf2.cbSize = sizeof(CHARFORMAT2A);
6216 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
6217 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
6218 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
6219 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6220 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6222 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
6223 already at 0. */
6224 received_WM_NOTIFY = 0;
6225 modify_at_WM_NOTIFY = 0;
6226 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6227 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6228 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6230 received_WM_NOTIFY = 0;
6231 modify_at_WM_NOTIFY = 0;
6232 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
6233 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6235 received_WM_NOTIFY = 0;
6236 modify_at_WM_NOTIFY = 0;
6237 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6238 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6239 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6241 /* Test for WM_NOTIFY messages with redraw disabled. */
6242 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6243 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
6244 received_WM_NOTIFY = 0;
6245 SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
6246 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6247 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
6249 /* Test filtering key events. */
6250 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6251 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
6252 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6253 received_WM_NOTIFY = 0;
6254 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6255 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6256 ok(sel_start == 1 && sel_end == 1,
6257 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6258 filter_on_WM_NOTIFY = TRUE;
6259 received_WM_NOTIFY = 0;
6260 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6261 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6262 ok(sel_start == 1 && sel_end == 1,
6263 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6265 /* test with owner set to NULL */
6266 SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
6267 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6268 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6269 ok(sel_start == 1 && sel_end == 1,
6270 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6272 DestroyWindow(hwndRichedit_WM_NOTIFY);
6273 DestroyWindow(parent);
6276 static ENLINK enlink;
6277 #define CURSOR_CLIENT_X 5
6278 #define CURSOR_CLIENT_Y 5
6279 #define WP_PARENT 1
6280 #define WP_CHILD 2
6282 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6284 if(message == WM_NOTIFY && ((NMHDR*)lParam)->code == EN_LINK)
6286 enlink = *(ENLINK*)lParam;
6288 return DefWindowProcA(hwnd, message, wParam, lParam);
6291 static void link_notify_test(const char *desc, int i, HWND hwnd, HWND parent,
6292 UINT msg, WPARAM wParam, LPARAM lParam, BOOL notifies)
6294 ENLINK junk_enlink;
6296 switch (msg)
6298 case WM_LBUTTONDBLCLK:
6299 case WM_LBUTTONDOWN:
6300 case WM_LBUTTONUP:
6301 case WM_MOUSEHOVER:
6302 case WM_MOUSEMOVE:
6303 case WM_MOUSEWHEEL:
6304 case WM_RBUTTONDBLCLK:
6305 case WM_RBUTTONDOWN:
6306 case WM_RBUTTONUP:
6307 lParam = MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y);
6308 break;
6309 case WM_SETCURSOR:
6310 if (wParam == WP_PARENT)
6311 wParam = (WPARAM)parent;
6312 else if (wParam == WP_CHILD)
6313 wParam = (WPARAM)hwnd;
6314 break;
6317 memset(&junk_enlink, 0x23, sizeof(junk_enlink));
6318 enlink = junk_enlink;
6320 SendMessageA(hwnd, msg, wParam, lParam);
6322 if (notifies)
6324 ok(enlink.nmhdr.hwndFrom == hwnd,
6325 "%s test %i: Expected hwnd %p got %p\n", desc, i, hwnd, enlink.nmhdr.hwndFrom);
6326 ok(enlink.nmhdr.idFrom == 0,
6327 "%s test %i: Expected idFrom 0 got 0x%lx\n", desc, i, enlink.nmhdr.idFrom);
6328 ok(enlink.msg == msg,
6329 "%s test %i: Expected msg 0x%x got 0x%x\n", desc, i, msg, enlink.msg);
6330 if (msg == WM_SETCURSOR)
6332 ok(enlink.wParam == 0,
6333 "%s test %i: Expected wParam 0 got 0x%lx\n", desc, i, enlink.wParam);
6335 else
6337 ok(enlink.wParam == wParam,
6338 "%s test %i: Expected wParam 0x%lx got 0x%lx\n", desc, i, wParam, enlink.wParam);
6340 ok(enlink.lParam == MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y),
6341 "%s test %i: Expected lParam 0x%lx got 0x%lx\n",
6342 desc, i, MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y), enlink.lParam);
6343 ok(enlink.chrg.cpMin == 0 && enlink.chrg.cpMax == 31,
6344 "%s test %i: Expected link range [0,31) got [%i,%i)\n", desc, i, enlink.chrg.cpMin, enlink.chrg.cpMax);
6346 else
6348 ok(memcmp(&enlink, &junk_enlink, sizeof(enlink)) == 0,
6349 "%s test %i: Expected enlink to remain unmodified\n", desc, i);
6353 static void test_EN_LINK(void)
6355 HWND hwnd, parent;
6356 WNDCLASSA cls;
6357 CHARFORMAT2A cf2;
6358 POINT orig_cursor_pos;
6359 POINT cursor_screen_pos = {CURSOR_CLIENT_X, CURSOR_CLIENT_Y};
6360 int i;
6362 static const struct
6364 UINT msg;
6365 WPARAM wParam;
6366 LPARAM lParam;
6367 BOOL notifies;
6369 link_notify_tests[] =
6371 /* hold down the left button and try some messages */
6372 { WM_LBUTTONDOWN, 0, 0, TRUE }, /* 0 */
6373 { EM_LINESCROLL, 0, 1, FALSE },
6374 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6375 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6376 { WM_MOUSEHOVER, 0, 0, FALSE },
6377 { WM_MOUSEMOVE, 0, 0, FALSE },
6378 { WM_MOUSEWHEEL, 0, 0, FALSE },
6379 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6380 { WM_RBUTTONDOWN, 0, 0, TRUE },
6381 { WM_RBUTTONUP, 0, 0, TRUE },
6382 { WM_SETCURSOR, 0, 0, FALSE },
6383 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6384 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6385 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6386 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6387 { WM_LBUTTONUP, 0, 0, TRUE },
6388 /* hold down the right button and try some messages */
6389 { WM_RBUTTONDOWN, 0, 0, TRUE }, /* 16 */
6390 { EM_LINESCROLL, 0, 1, FALSE },
6391 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6392 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6393 { WM_LBUTTONDOWN, 0, 0, TRUE },
6394 { WM_LBUTTONUP, 0, 0, TRUE },
6395 { WM_MOUSEHOVER, 0, 0, FALSE },
6396 { WM_MOUSEMOVE, 0, 0, TRUE },
6397 { WM_MOUSEWHEEL, 0, 0, FALSE },
6398 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6399 { WM_SETCURSOR, 0, 0, FALSE },
6400 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6401 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6402 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6403 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6404 { WM_RBUTTONUP, 0, 0, TRUE },
6405 /* try the messages with both buttons released */
6406 { EM_LINESCROLL, 0, 1, FALSE }, /* 32 */
6407 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6408 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6409 { WM_LBUTTONDOWN, 0, 0, TRUE },
6410 { WM_LBUTTONUP, 0, 0, TRUE },
6411 { WM_MOUSEHOVER, 0, 0, FALSE },
6412 { WM_MOUSEMOVE, 0, 0, TRUE },
6413 { WM_MOUSEWHEEL, 0, 0, FALSE },
6414 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6415 { WM_RBUTTONDOWN, 0, 0, TRUE },
6416 { WM_RBUTTONUP, 0, 0, TRUE },
6417 { WM_SETCURSOR, 0, 0, FALSE },
6418 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6419 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6420 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6421 { WM_VSCROLL, SB_BOTTOM, 0, FALSE }
6424 /* register class to capture WM_NOTIFY */
6425 cls.style = 0;
6426 cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
6427 cls.cbClsExtra = 0;
6428 cls.cbWndExtra = 0;
6429 cls.hInstance = GetModuleHandleA(0);
6430 cls.hIcon = 0;
6431 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6432 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6433 cls.lpszMenuName = NULL;
6434 cls.lpszClassName = "EN_LINK_ParentClass";
6435 if(!RegisterClassA(&cls)) assert(0);
6437 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6438 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6439 ok(parent != 0, "Failed to create parent window\n");
6441 hwnd = new_richedit(parent);
6442 ok(hwnd != 0, "Failed to create edit window\n");
6444 SendMessageA(hwnd, EM_SETEVENTMASK, 0, ENM_LINK);
6446 cf2.cbSize = sizeof(CHARFORMAT2A);
6447 cf2.dwMask = CFM_LINK;
6448 cf2.dwEffects = CFE_LINK;
6449 SendMessageA(hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6450 /* mixing letters and numbers causes runs to be split */
6451 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
6453 GetCursorPos(&orig_cursor_pos);
6454 SetCursorPos(0, 0);
6456 for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6458 link_notify_test("cursor position simulated", i, hwnd, parent,
6459 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6460 link_notify_tests[i].msg == WM_SETCURSOR ? FALSE : link_notify_tests[i].notifies);
6463 ClientToScreen(hwnd, &cursor_screen_pos);
6464 SetCursorPos(cursor_screen_pos.x, cursor_screen_pos.y);
6466 for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6468 link_notify_test("cursor position set", i, hwnd, parent,
6469 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6470 link_notify_tests[i].notifies);
6473 SetCursorPos(orig_cursor_pos.x, orig_cursor_pos.y);
6474 DestroyWindow(hwnd);
6475 DestroyWindow(parent);
6478 static void test_undo_coalescing(void)
6480 HWND hwnd;
6481 int result;
6482 char buffer[64] = {0};
6484 /* multi-line control inserts CR normally */
6485 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6486 0, 0, 200, 60, 0, 0, 0, 0);
6487 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6489 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6490 ok (result == FALSE, "Can undo after window creation.\n");
6491 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6492 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6493 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6494 ok (result == FALSE, "Can redo after window creation.\n");
6495 result = SendMessageA(hwnd, EM_REDO, 0, 0);
6496 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6498 /* Test the effect of arrows keys during typing on undo transactions*/
6499 simulate_typing_characters(hwnd, "one two three");
6500 SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6501 SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6502 simulate_typing_characters(hwnd, " four five six");
6504 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6505 ok (result == FALSE, "Can redo before anything is undone.\n");
6506 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6507 ok (result == TRUE, "Cannot undo typed characters.\n");
6508 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6509 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6510 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6511 ok (result == TRUE, "Cannot redo after undo.\n");
6512 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6513 result = strcmp(buffer, "one two three");
6514 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6516 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6517 ok (result == TRUE, "Cannot undo typed characters.\n");
6518 result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6519 ok (result == TRUE, "Failed to undo typed characters.\n");
6520 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6521 result = strcmp(buffer, "");
6522 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6524 /* Test the effect of focus changes during typing on undo transactions*/
6525 simulate_typing_characters(hwnd, "one two three");
6526 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6527 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6528 SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6529 SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6530 simulate_typing_characters(hwnd, " four five six");
6531 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6532 ok (result == TRUE, "Failed to undo typed characters.\n");
6533 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6534 result = strcmp(buffer, "one two three");
6535 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6537 /* Test the effect of the back key during typing on undo transactions */
6538 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6539 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6540 ok (result == TRUE, "Failed to clear the text.\n");
6541 simulate_typing_characters(hwnd, "one two threa");
6542 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6543 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6544 SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
6545 SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
6546 simulate_typing_characters(hwnd, "e four five six");
6547 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6548 ok (result == TRUE, "Failed to undo typed characters.\n");
6549 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6550 result = strcmp(buffer, "");
6551 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6553 /* Test the effect of the delete key during typing on undo transactions */
6554 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6555 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
6556 ok(result == TRUE, "Failed to set the text.\n");
6557 SendMessageA(hwnd, EM_SETSEL, 1, 1);
6558 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6559 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6560 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6561 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6562 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6563 ok (result == TRUE, "Failed to undo typed characters.\n");
6564 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6565 result = strcmp(buffer, "acd");
6566 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
6567 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6568 ok (result == TRUE, "Failed to undo typed characters.\n");
6569 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6570 result = strcmp(buffer, "abcd");
6571 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
6573 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
6574 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6575 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6576 ok (result == TRUE, "Failed to clear the text.\n");
6577 simulate_typing_characters(hwnd, "one two three");
6578 result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
6579 ok (result == 0, "expected %d but got %d\n", 0, result);
6580 simulate_typing_characters(hwnd, " four five six");
6581 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6582 ok (result == TRUE, "Failed to undo typed characters.\n");
6583 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6584 result = strcmp(buffer, "one two three");
6585 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6586 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6587 ok (result == TRUE, "Failed to undo typed characters.\n");
6588 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6589 result = strcmp(buffer, "");
6590 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6592 DestroyWindow(hwnd);
6595 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
6597 int length;
6599 /* MSDN lied, length is actually the number of bytes. */
6600 length = bytes / sizeof(WCHAR);
6601 switch(code)
6603 case WB_ISDELIMITER:
6604 return text[pos] == 'X';
6605 case WB_LEFT:
6606 case WB_MOVEWORDLEFT:
6607 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6608 return pos-1;
6609 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
6610 case WB_LEFTBREAK:
6611 pos--;
6612 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6613 pos--;
6614 return pos;
6615 case WB_RIGHT:
6616 case WB_MOVEWORDRIGHT:
6617 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6618 return pos+1;
6619 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
6620 case WB_RIGHTBREAK:
6621 pos++;
6622 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6623 pos++;
6624 return pos;
6625 default:
6626 ok(FALSE, "Unexpected code %d\n", code);
6627 break;
6629 return 0;
6632 static void test_word_movement(void)
6634 HWND hwnd;
6635 int result;
6636 int sel_start, sel_end;
6637 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6639 /* multi-line control inserts CR normally */
6640 hwnd = new_richedit(NULL);
6642 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
6643 ok (result == TRUE, "Failed to clear the text.\n");
6644 SendMessageA(hwnd, EM_SETSEL, 0, 0);
6645 /* |one two three */
6647 send_ctrl_key(hwnd, VK_RIGHT);
6648 /* one |two three */
6649 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6650 ok(sel_start == sel_end, "Selection should be empty\n");
6651 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6653 send_ctrl_key(hwnd, VK_RIGHT);
6654 /* one two |three */
6655 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6656 ok(sel_start == sel_end, "Selection should be empty\n");
6657 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6659 send_ctrl_key(hwnd, VK_LEFT);
6660 /* one |two three */
6661 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6662 ok(sel_start == sel_end, "Selection should be empty\n");
6663 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6665 send_ctrl_key(hwnd, VK_LEFT);
6666 /* |one two three */
6667 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6668 ok(sel_start == sel_end, "Selection should be empty\n");
6669 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6671 SendMessageA(hwnd, EM_SETSEL, 8, 8);
6672 /* one two | three */
6673 send_ctrl_key(hwnd, VK_RIGHT);
6674 /* one two |three */
6675 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6676 ok(sel_start == sel_end, "Selection should be empty\n");
6677 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6679 SendMessageA(hwnd, EM_SETSEL, 11, 11);
6680 /* one two th|ree */
6681 send_ctrl_key(hwnd, VK_LEFT);
6682 /* one two |three */
6683 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6684 ok(sel_start == sel_end, "Selection should be empty\n");
6685 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6687 /* Test with a custom word break procedure that uses X as the delimiter. */
6688 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6689 ok (result == TRUE, "Failed to clear the text.\n");
6690 SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6691 /* |one twoXthree */
6692 send_ctrl_key(hwnd, VK_RIGHT);
6693 /* one twoX|three */
6694 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6695 ok(sel_start == sel_end, "Selection should be empty\n");
6696 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6698 DestroyWindow(hwnd);
6700 /* Make sure the behaviour is the same with a unicode richedit window,
6701 * and using unicode functions. */
6703 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6704 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6705 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6707 /* Test with a custom word break procedure that uses X as the delimiter. */
6708 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6709 ok (result == TRUE, "Failed to clear the text.\n");
6710 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6711 /* |one twoXthree */
6712 send_ctrl_key(hwnd, VK_RIGHT);
6713 /* one twoX|three */
6714 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6715 ok(sel_start == sel_end, "Selection should be empty\n");
6716 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6718 DestroyWindow(hwnd);
6721 static void test_EM_CHARFROMPOS(void)
6723 HWND hwnd;
6724 int result;
6725 RECT rcClient;
6726 POINTL point;
6727 point.x = 0;
6728 point.y = 40;
6730 /* multi-line control inserts CR normally */
6731 hwnd = new_richedit(NULL);
6732 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6733 (LPARAM)"one two three four five six seven\reight");
6734 ok(result == 1, "Expected 1, got %d\n", result);
6735 GetClientRect(hwnd, &rcClient);
6737 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6738 ok(result == 34, "expected character index of 34 but got %d\n", result);
6740 /* Test with points outside the bounds of the richedit control. */
6741 point.x = -1;
6742 point.y = 40;
6743 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6744 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6746 point.x = 1000;
6747 point.y = 0;
6748 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6749 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6751 point.x = 1000;
6752 point.y = 36;
6753 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6754 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6756 point.x = 1000;
6757 point.y = -1;
6758 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6759 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6761 point.x = 1000;
6762 point.y = rcClient.bottom + 1;
6763 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6764 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6766 point.x = 1000;
6767 point.y = rcClient.bottom;
6768 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6769 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6771 DestroyWindow(hwnd);
6774 static void test_word_wrap(void)
6776 HWND hwnd;
6777 POINTL point = {0, 60}; /* This point must be below the first line */
6778 const char *text = "Must be long enough to test line wrapping";
6779 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6780 int res, pos, lines;
6782 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6783 * when specified on window creation and set later. */
6784 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6785 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6786 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6787 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6788 ok(res, "WM_SETTEXT failed.\n");
6789 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6790 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6791 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6792 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6794 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6795 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6796 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6797 DestroyWindow(hwnd);
6799 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
6800 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6801 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6803 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6804 ok(res, "WM_SETTEXT failed.\n");
6805 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6806 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6807 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6808 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6810 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6811 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6812 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6813 DestroyWindow(hwnd);
6815 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6816 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6817 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6818 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6819 ok(res, "WM_SETTEXT failed.\n");
6820 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6821 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6823 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6824 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6825 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6826 DestroyWindow(hwnd);
6828 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
6829 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6830 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6831 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6832 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6833 ok(res, "WM_SETTEXT failed.\n");
6834 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6835 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6837 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6838 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6839 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6841 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6842 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
6843 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6844 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6845 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6847 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
6848 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6849 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6850 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6851 DestroyWindow(hwnd);
6853 /* Test to see if wrapping happens with redraw disabled. */
6854 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6855 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6856 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6857 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
6858 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
6859 ok(res, "EM_REPLACESEL failed.\n");
6860 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6861 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6862 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6863 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6864 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6866 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6867 DestroyWindow(hwnd);
6870 static void test_autoscroll(void)
6872 HWND hwnd = new_richedit(NULL);
6873 int lines, ret, redraw;
6874 POINT pt;
6876 for (redraw = 0; redraw <= 1; redraw++) {
6877 trace("testing with WM_SETREDRAW=%d\n", redraw);
6878 SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
6879 SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6880 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6881 ok(lines == 8, "%d lines instead of 8\n", lines);
6882 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6883 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6884 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6885 ret = GetWindowLongA(hwnd, GWL_STYLE);
6886 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6888 SendMessageA(hwnd, WM_SETTEXT, 0, 0);
6889 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6890 ok(lines == 1, "%d lines instead of 1\n", lines);
6891 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6892 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6893 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6894 ret = GetWindowLongA(hwnd, GWL_STYLE);
6895 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6898 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6899 DestroyWindow(hwnd);
6901 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6902 * auto vertical/horizontal scrolling options. */
6903 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6904 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6905 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6906 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6907 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6908 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6909 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6910 ret = GetWindowLongA(hwnd, GWL_STYLE);
6911 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6912 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6913 DestroyWindow(hwnd);
6915 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6916 WS_POPUP|ES_MULTILINE,
6917 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6918 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6919 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6920 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6921 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6922 ret = GetWindowLongA(hwnd, GWL_STYLE);
6923 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6924 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6925 DestroyWindow(hwnd);
6929 static void test_format_rect(void)
6931 HWND hwnd;
6932 RECT rc, expected, clientRect;
6933 int n;
6934 DWORD options;
6936 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6937 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6938 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6939 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6941 GetClientRect(hwnd, &clientRect);
6943 expected = clientRect;
6944 expected.left += 1;
6945 expected.right -= 1;
6946 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6947 ok(rc.top == expected.top && rc.left == expected.left &&
6948 rc.bottom == expected.bottom && rc.right == expected.right,
6949 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6950 rc.top, rc.left, rc.bottom, rc.right,
6951 expected.top, expected.left, expected.bottom, expected.right);
6953 for (n = -3; n <= 3; n++)
6955 rc = clientRect;
6956 rc.top += n;
6957 rc.left += n;
6958 rc.bottom -= n;
6959 rc.right -= n;
6960 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6962 expected = rc;
6963 expected.top = max(0, rc.top);
6964 expected.left = max(0, rc.left);
6965 expected.bottom = min(clientRect.bottom, rc.bottom);
6966 expected.right = min(clientRect.right, rc.right);
6967 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6968 ok(rc.top == expected.top && rc.left == expected.left &&
6969 rc.bottom == expected.bottom && rc.right == expected.right,
6970 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6971 n, rc.top, rc.left, rc.bottom, rc.right,
6972 expected.top, expected.left, expected.bottom, expected.right);
6975 rc = clientRect;
6976 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6977 expected = clientRect;
6978 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6979 ok(rc.top == expected.top && rc.left == expected.left &&
6980 rc.bottom == expected.bottom && rc.right == expected.right,
6981 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6982 rc.top, rc.left, rc.bottom, rc.right,
6983 expected.top, expected.left, expected.bottom, expected.right);
6985 /* Adding the selectionbar adds the selectionbar width to the left side. */
6986 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6987 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6988 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6989 expected.left += 8; /* selection bar width */
6990 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6991 ok(rc.top == expected.top && rc.left == expected.left &&
6992 rc.bottom == expected.bottom && rc.right == expected.right,
6993 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6994 rc.top, rc.left, rc.bottom, rc.right,
6995 expected.top, expected.left, expected.bottom, expected.right);
6997 rc = clientRect;
6998 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6999 expected = clientRect;
7000 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7001 ok(rc.top == expected.top && rc.left == expected.left &&
7002 rc.bottom == expected.bottom && rc.right == expected.right,
7003 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7004 rc.top, rc.left, rc.bottom, rc.right,
7005 expected.top, expected.left, expected.bottom, expected.right);
7007 /* Removing the selectionbar subtracts the selectionbar width from the left side,
7008 * even if the left side is already 0. */
7009 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
7010 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7011 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
7012 expected.left -= 8; /* selection bar width */
7013 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7014 ok(rc.top == expected.top && rc.left == expected.left &&
7015 rc.bottom == expected.bottom && rc.right == expected.right,
7016 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7017 rc.top, rc.left, rc.bottom, rc.right,
7018 expected.top, expected.left, expected.bottom, expected.right);
7020 /* Set the absolute value of the formatting rectangle. */
7021 rc = clientRect;
7022 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7023 expected = clientRect;
7024 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7025 ok(rc.top == expected.top && rc.left == expected.left &&
7026 rc.bottom == expected.bottom && rc.right == expected.right,
7027 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7028 n, rc.top, rc.left, rc.bottom, rc.right,
7029 expected.top, expected.left, expected.bottom, expected.right);
7031 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
7032 * LPARAM as being a relative offset when the WPARAM value is 1, but these
7033 * tests show that this isn't true. */
7034 rc.top = 15;
7035 rc.left = 15;
7036 rc.bottom = clientRect.bottom - 15;
7037 rc.right = clientRect.right - 15;
7038 expected = rc;
7039 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7040 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7041 ok(rc.top == expected.top && rc.left == expected.left &&
7042 rc.bottom == expected.bottom && rc.right == expected.right,
7043 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7044 rc.top, rc.left, rc.bottom, rc.right,
7045 expected.top, expected.left, expected.bottom, expected.right);
7047 /* For some reason it does not limit the values to the client rect with
7048 * a WPARAM value of 1. */
7049 rc.top = -15;
7050 rc.left = -15;
7051 rc.bottom = clientRect.bottom + 15;
7052 rc.right = clientRect.right + 15;
7053 expected = rc;
7054 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7055 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7056 ok(rc.top == expected.top && rc.left == expected.left &&
7057 rc.bottom == expected.bottom && rc.right == expected.right,
7058 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7059 rc.top, rc.left, rc.bottom, rc.right,
7060 expected.top, expected.left, expected.bottom, expected.right);
7062 /* Reset to default rect and check how the format rect adjusts to window
7063 * resize and how it copes with very small windows */
7064 SendMessageA(hwnd, EM_SETRECT, 0, 0);
7066 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
7067 GetClientRect(hwnd, &clientRect);
7069 expected = clientRect;
7070 expected.left += 1;
7071 expected.right -= 1;
7072 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7073 ok(rc.top == expected.top && rc.left == expected.left &&
7074 rc.bottom == expected.bottom && rc.right == expected.right,
7075 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7076 rc.top, rc.left, rc.bottom, rc.right,
7077 expected.top, expected.left, expected.bottom, expected.right);
7079 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
7080 GetClientRect(hwnd, &clientRect);
7082 expected = clientRect;
7083 expected.left += 1;
7084 expected.right -= 1;
7085 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7086 ok(rc.top == expected.top && rc.left == expected.left &&
7087 rc.bottom == expected.bottom && rc.right == expected.right,
7088 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7089 rc.top, rc.left, rc.bottom, rc.right,
7090 expected.top, expected.left, expected.bottom, expected.right);
7092 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
7093 GetClientRect(hwnd, &clientRect);
7095 expected = clientRect;
7096 expected.left += 1;
7097 expected.right -= 1;
7098 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7099 ok(rc.top == expected.top && rc.left == expected.left &&
7100 rc.bottom == expected.bottom && rc.right == expected.right,
7101 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7102 rc.top, rc.left, rc.bottom, rc.right,
7103 expected.top, expected.left, expected.bottom, expected.right);
7105 DestroyWindow(hwnd);
7107 /* The extended window style affects the formatting rectangle. */
7108 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
7109 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7110 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7111 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7113 GetClientRect(hwnd, &clientRect);
7115 expected = clientRect;
7116 expected.left += 1;
7117 expected.top += 1;
7118 expected.right -= 1;
7119 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7120 ok(rc.top == expected.top && rc.left == expected.left &&
7121 rc.bottom == expected.bottom && rc.right == expected.right,
7122 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7123 rc.top, rc.left, rc.bottom, rc.right,
7124 expected.top, expected.left, expected.bottom, expected.right);
7126 rc = clientRect;
7127 rc.top += 5;
7128 rc.left += 5;
7129 rc.bottom -= 5;
7130 rc.right -= 5;
7131 expected = rc;
7132 expected.top -= 1;
7133 expected.left -= 1;
7134 expected.right += 1;
7135 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7136 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7137 ok(rc.top == expected.top && rc.left == expected.left &&
7138 rc.bottom == expected.bottom && rc.right == expected.right,
7139 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7140 rc.top, rc.left, rc.bottom, rc.right,
7141 expected.top, expected.left, expected.bottom, expected.right);
7143 DestroyWindow(hwnd);
7146 static void test_WM_GETDLGCODE(void)
7148 HWND hwnd;
7149 UINT res, expected;
7150 MSG msg;
7152 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7154 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7155 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7156 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7157 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7158 msg.hwnd = hwnd;
7159 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
7160 expected = expected | DLGC_WANTMESSAGE;
7161 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7162 res, expected);
7163 DestroyWindow(hwnd);
7165 msg.message = WM_KEYDOWN;
7166 msg.wParam = VK_RETURN;
7167 msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7168 msg.pt.x = 0;
7169 msg.pt.y = 0;
7170 msg.time = GetTickCount();
7172 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7173 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7174 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7175 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7176 msg.hwnd = hwnd;
7177 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7178 expected = expected | DLGC_WANTMESSAGE;
7179 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7180 res, expected);
7181 DestroyWindow(hwnd);
7183 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7184 ES_MULTILINE|WS_POPUP,
7185 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7186 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7187 msg.hwnd = hwnd;
7188 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7189 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7190 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7191 res, expected);
7192 DestroyWindow(hwnd);
7194 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7195 ES_WANTRETURN|WS_POPUP,
7196 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7197 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7198 msg.hwnd = hwnd;
7199 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7200 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7201 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7202 res, expected);
7203 DestroyWindow(hwnd);
7205 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7206 WS_POPUP,
7207 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7208 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7209 msg.hwnd = hwnd;
7210 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7211 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7212 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7213 res, expected);
7214 DestroyWindow(hwnd);
7216 msg.wParam = VK_TAB;
7217 msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7219 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7220 ES_MULTILINE|WS_POPUP,
7221 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7222 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7223 msg.hwnd = hwnd;
7224 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7225 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7226 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7227 res, expected);
7228 DestroyWindow(hwnd);
7230 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7231 WS_POPUP,
7232 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7233 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7234 msg.hwnd = hwnd;
7235 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7236 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7237 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7238 res, expected);
7239 DestroyWindow(hwnd);
7241 hold_key(VK_CONTROL);
7243 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7244 ES_MULTILINE|WS_POPUP,
7245 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7246 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7247 msg.hwnd = hwnd;
7248 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7249 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7250 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7251 res, expected);
7252 DestroyWindow(hwnd);
7254 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7255 WS_POPUP,
7256 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7257 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7258 msg.hwnd = hwnd;
7259 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7260 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7261 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7262 res, expected);
7263 DestroyWindow(hwnd);
7265 release_key(VK_CONTROL);
7267 msg.wParam = 'a';
7268 msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
7270 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7271 ES_MULTILINE|WS_POPUP,
7272 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7273 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7274 msg.hwnd = hwnd;
7275 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7276 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7277 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7278 res, expected);
7279 DestroyWindow(hwnd);
7281 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7282 WS_POPUP,
7283 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7284 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7285 msg.hwnd = hwnd;
7286 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7287 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7288 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7289 res, expected);
7290 DestroyWindow(hwnd);
7292 msg.message = WM_CHAR;
7294 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7295 ES_MULTILINE|WS_POPUP,
7296 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7297 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7298 msg.hwnd = hwnd;
7299 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7300 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7301 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7302 res, expected);
7303 DestroyWindow(hwnd);
7305 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7306 WS_POPUP,
7307 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7308 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7309 msg.hwnd = hwnd;
7310 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7311 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7312 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7313 res, expected);
7314 DestroyWindow(hwnd);
7316 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7317 WS_POPUP|ES_SAVESEL,
7318 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7319 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7320 res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
7321 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
7322 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7323 res, expected);
7324 DestroyWindow(hwnd);
7327 static void test_zoom(void)
7329 HWND hwnd;
7330 UINT ret;
7331 RECT rc;
7332 POINT pt;
7333 int numerator, denominator;
7335 hwnd = new_richedit(NULL);
7336 GetClientRect(hwnd, &rc);
7337 pt.x = (rc.right - rc.left) / 2;
7338 pt.y = (rc.bottom - rc.top) / 2;
7339 ClientToScreen(hwnd, &pt);
7341 /* Test initial zoom value */
7342 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7343 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
7344 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
7345 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7347 /* test scroll wheel */
7348 hold_key(VK_CONTROL);
7349 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7350 MAKELPARAM(pt.x, pt.y));
7351 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7352 release_key(VK_CONTROL);
7354 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7355 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
7356 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7357 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7359 /* Test how much the mouse wheel can zoom in and out. */
7360 ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
7361 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7363 hold_key(VK_CONTROL);
7364 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7365 MAKELPARAM(pt.x, pt.y));
7366 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7367 release_key(VK_CONTROL);
7369 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7370 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
7371 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7372 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7374 ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
7375 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7377 hold_key(VK_CONTROL);
7378 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7379 MAKELPARAM(pt.x, pt.y));
7380 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7381 release_key(VK_CONTROL);
7383 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7384 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
7385 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7386 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7388 ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
7389 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7391 hold_key(VK_CONTROL);
7392 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7393 MAKELPARAM(pt.x, pt.y));
7394 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7395 release_key(VK_CONTROL);
7397 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7398 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
7399 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7400 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7402 ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
7403 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7405 hold_key(VK_CONTROL);
7406 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7407 MAKELPARAM(pt.x, pt.y));
7408 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7409 release_key(VK_CONTROL);
7411 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7412 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
7413 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7414 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7416 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
7417 ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
7418 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7420 hold_key(VK_CONTROL);
7421 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7422 MAKELPARAM(pt.x, pt.y));
7423 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7424 release_key(VK_CONTROL);
7426 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7427 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
7428 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7429 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7431 /* Test bounds checking on EM_SETZOOM */
7432 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
7433 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7435 ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
7436 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7438 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
7439 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7441 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7442 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7443 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7444 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7446 ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
7447 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7449 /* See if negative numbers are accepted. */
7450 ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
7451 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7453 /* See if negative numbers are accepted. */
7454 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
7455 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
7457 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7458 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7459 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7460 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7462 /* Reset the zoom value */
7463 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
7464 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7466 DestroyWindow(hwnd);
7469 struct dialog_mode_messages
7471 int wm_getdefid, wm_close, wm_nextdlgctl;
7474 static struct dialog_mode_messages dm_messages;
7476 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
7477 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
7478 "got %d\n", wmclose, dm_messages.wm_close); \
7479 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
7480 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
7481 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7482 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7484 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7486 switch (iMsg)
7488 case DM_GETDEFID:
7489 dm_messages.wm_getdefid++;
7490 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7491 case WM_NEXTDLGCTL:
7492 dm_messages.wm_nextdlgctl++;
7493 break;
7494 case WM_CLOSE:
7495 dm_messages.wm_close++;
7496 break;
7499 return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7502 static void test_dialogmode(void)
7504 HWND hwRichEdit, hwParent, hwButton;
7505 MSG msg= {0};
7506 int lcount, r;
7507 WNDCLASSA cls;
7509 cls.style = 0;
7510 cls.lpfnWndProc = dialog_mode_wnd_proc;
7511 cls.cbClsExtra = 0;
7512 cls.cbWndExtra = 0;
7513 cls.hInstance = GetModuleHandleA(0);
7514 cls.hIcon = 0;
7515 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7516 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7517 cls.lpszMenuName = NULL;
7518 cls.lpszClassName = "DialogModeParentClass";
7519 if(!RegisterClassA(&cls)) assert(0);
7521 hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7522 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7524 /* Test richedit(ES_MULTILINE) */
7526 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7528 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7529 ok(0 == r, "expected 0, got %d\n", r);
7530 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7531 ok(2 == lcount, "expected 2, got %d\n", lcount);
7533 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7534 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7536 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7537 ok(0 == r, "expected 0, got %d\n", r);
7538 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7539 ok(3 == lcount, "expected 3, got %d\n", lcount);
7541 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7542 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7543 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7544 ok(0 == r, "expected 0, got %d\n", r);
7545 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7546 ok(3 == lcount, "expected 3, got %d\n", lcount);
7548 DestroyWindow(hwRichEdit);
7550 /* Test standalone richedit(ES_MULTILINE) */
7552 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7554 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7555 ok(0 == r, "expected 0, got %d\n", r);
7556 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7557 ok(2 == lcount, "expected 2, got %d\n", lcount);
7559 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7560 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7562 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7563 ok(0 == r, "expected 0, got %d\n", r);
7564 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7565 ok(2 == lcount, "expected 2, got %d\n", lcount);
7567 DestroyWindow(hwRichEdit);
7569 /* Check a destination for messages */
7571 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7573 SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7574 SetParent( hwRichEdit, NULL);
7576 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7577 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
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, 1, 0);
7584 memset(&dm_messages, 0, sizeof(dm_messages));
7585 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7586 ok(0 == r, "expected 0, got %d\n", r);
7587 test_dm_messages(0, 0, 1);
7589 DestroyWindow(hwRichEdit);
7591 /* Check messages from richedit(ES_MULTILINE) */
7593 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7595 memset(&dm_messages, 0, sizeof(dm_messages));
7596 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7597 ok(0 == r, "expected 0, got %d\n", r);
7598 test_dm_messages(0, 0, 0);
7600 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7601 ok(2 == lcount, "expected 2, got %d\n", lcount);
7603 memset(&dm_messages, 0, sizeof(dm_messages));
7604 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7605 ok(0 == r, "expected 0, got %d\n", r);
7606 test_dm_messages(0, 0, 0);
7608 memset(&dm_messages, 0, sizeof(dm_messages));
7609 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7610 ok(0 == r, "expected 0, got %d\n", r);
7611 test_dm_messages(0, 0, 0);
7613 memset(&dm_messages, 0, sizeof(dm_messages));
7614 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7615 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7616 test_dm_messages(0, 0, 0);
7618 memset(&dm_messages, 0, sizeof(dm_messages));
7619 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7620 ok(0 == r, "expected 0, got %d\n", r);
7621 test_dm_messages(0, 1, 0);
7623 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7624 ok(2 == lcount, "expected 2, got %d\n", lcount);
7626 memset(&dm_messages, 0, sizeof(dm_messages));
7627 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
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_KEYDOWN, VK_TAB, 0xf0001);
7633 ok(0 == r, "expected 0, got %d\n", r);
7634 test_dm_messages(0, 0, 1);
7636 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7637 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7638 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7640 memset(&dm_messages, 0, sizeof(dm_messages));
7641 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7642 ok(0 == r, "expected 0, got %d\n", r);
7643 test_dm_messages(0, 1, 1);
7645 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7646 ok(2 == lcount, "expected 2, got %d\n", lcount);
7648 DestroyWindow(hwButton);
7649 DestroyWindow(hwRichEdit);
7651 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7653 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
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, 0, 0);
7660 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7661 ok(2 == lcount, "expected 2, got %d\n", lcount);
7663 memset(&dm_messages, 0, sizeof(dm_messages));
7664 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7665 ok(0 == r, "expected 0, got %d\n", r);
7666 test_dm_messages(0, 0, 0);
7668 memset(&dm_messages, 0, sizeof(dm_messages));
7669 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7670 ok(0 == r, "expected 0, got %d\n", r);
7671 test_dm_messages(0, 0, 0);
7673 memset(&dm_messages, 0, sizeof(dm_messages));
7674 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7675 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7676 test_dm_messages(0, 0, 0);
7678 memset(&dm_messages, 0, sizeof(dm_messages));
7679 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7680 ok(0 == r, "expected 0, got %d\n", r);
7681 test_dm_messages(0, 0, 0);
7683 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7684 ok(3 == lcount, "expected 3, got %d\n", lcount);
7686 memset(&dm_messages, 0, sizeof(dm_messages));
7687 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7688 ok(0 == r, "expected 0, got %d\n", r);
7689 test_dm_messages(0, 0, 0);
7691 memset(&dm_messages, 0, sizeof(dm_messages));
7692 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7693 ok(0 == r, "expected 0, got %d\n", r);
7694 test_dm_messages(0, 0, 1);
7696 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7697 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7698 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7700 memset(&dm_messages, 0, sizeof(dm_messages));
7701 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7702 ok(0 == r, "expected 0, got %d\n", r);
7703 test_dm_messages(0, 0, 0);
7705 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7706 ok(4 == lcount, "expected 4, got %d\n", lcount);
7708 DestroyWindow(hwButton);
7709 DestroyWindow(hwRichEdit);
7711 /* Check messages from richedit(0) */
7713 hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
7715 memset(&dm_messages, 0, sizeof(dm_messages));
7716 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7717 ok(0 == r, "expected 0, got %d\n", r);
7718 test_dm_messages(0, 0, 0);
7720 memset(&dm_messages, 0, sizeof(dm_messages));
7721 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7722 ok(0 == r, "expected 0, got %d\n", r);
7723 test_dm_messages(0, 0, 0);
7725 memset(&dm_messages, 0, sizeof(dm_messages));
7726 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7727 ok(0 == r, "expected 0, got %d\n", r);
7728 test_dm_messages(0, 0, 0);
7730 memset(&dm_messages, 0, sizeof(dm_messages));
7731 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7732 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7733 test_dm_messages(0, 0, 0);
7735 memset(&dm_messages, 0, sizeof(dm_messages));
7736 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7737 ok(0 == r, "expected 0, got %d\n", r);
7738 test_dm_messages(0, 1, 0);
7740 memset(&dm_messages, 0, sizeof(dm_messages));
7741 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7742 ok(0 == r, "expected 0, got %d\n", r);
7743 test_dm_messages(0, 0, 0);
7745 memset(&dm_messages, 0, sizeof(dm_messages));
7746 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7747 ok(0 == r, "expected 0, got %d\n", r);
7748 test_dm_messages(0, 0, 1);
7750 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7751 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7752 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7754 memset(&dm_messages, 0, sizeof(dm_messages));
7755 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7756 ok(0 == r, "expected 0, got %d\n", r);
7757 test_dm_messages(0, 1, 1);
7759 DestroyWindow(hwRichEdit);
7761 /* Check messages from richedit(ES_WANTRETURN) */
7763 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
7765 memset(&dm_messages, 0, sizeof(dm_messages));
7766 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7767 ok(0 == r, "expected 0, got %d\n", r);
7768 test_dm_messages(0, 0, 0);
7770 memset(&dm_messages, 0, sizeof(dm_messages));
7771 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7772 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7773 test_dm_messages(0, 0, 0);
7775 memset(&dm_messages, 0, sizeof(dm_messages));
7776 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7777 ok(0 == r, "expected 0, got %d\n", r);
7778 test_dm_messages(0, 0, 0);
7780 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7781 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7782 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7784 memset(&dm_messages, 0, sizeof(dm_messages));
7785 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7786 ok(0 == r, "expected 0, got %d\n", r);
7787 test_dm_messages(0, 0, 0);
7789 DestroyWindow(hwRichEdit);
7790 DestroyWindow(hwParent);
7793 static void test_EM_FINDWORDBREAK_W(void)
7795 static const struct {
7796 WCHAR c;
7797 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7798 } delimiter_tests[] = {
7799 {0x0a, FALSE}, /* newline */
7800 {0x0b, FALSE}, /* vertical tab */
7801 {0x0c, FALSE}, /* form feed */
7802 {0x0d, FALSE}, /* carriage return */
7803 {0x20, TRUE}, /* space */
7804 {0x61, FALSE}, /* capital letter a */
7805 {0xa0, FALSE}, /* no-break space */
7806 {0x2000, FALSE}, /* en quad */
7807 {0x3000, FALSE}, /* Ideographic space */
7808 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7809 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7810 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7811 {0xac00, FALSE}, /* Hangul character GA*/
7812 {0xd7af, FALSE}, /* End of Hangul character chart */
7813 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7814 {0xff20, FALSE}, /* fullwidth commercial @ */
7815 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7817 int i;
7818 HWND hwndRichEdit = new_richeditW(NULL);
7819 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7820 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7822 WCHAR wbuf[2];
7823 int result;
7825 wbuf[0] = delimiter_tests[i].c;
7826 wbuf[1] = 0;
7827 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7828 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7829 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7830 todo_wine
7831 ok(result == delimiter_tests[i].isdelimiter,
7832 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7833 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7834 else
7835 ok(result == delimiter_tests[i].isdelimiter,
7836 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7837 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7839 DestroyWindow(hwndRichEdit);
7842 static void test_EM_FINDWORDBREAK_A(void)
7844 static const struct {
7845 WCHAR c;
7846 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7847 } delimiter_tests[] = {
7848 {0x0a, FALSE}, /* newline */
7849 {0x0b, FALSE}, /* vertical tab */
7850 {0x0c, FALSE}, /* form feed */
7851 {0x0d, FALSE}, /* carriage return */
7852 {0x20, TRUE}, /* space */
7853 {0x61, FALSE}, /* capital letter a */
7855 int i;
7856 HWND hwndRichEdit = new_richedit(NULL);
7858 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7859 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7861 int result;
7862 char buf[2];
7863 buf[0] = delimiter_tests[i].c;
7864 buf[1] = 0;
7865 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7866 result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7867 if (buf[0] == 0x20)
7868 todo_wine
7869 ok(result == delimiter_tests[i].isdelimiter,
7870 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7871 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7872 else
7873 ok(result == delimiter_tests[i].isdelimiter,
7874 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7875 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7877 DestroyWindow(hwndRichEdit);
7881 * This test attempts to show the effect of enter on a richedit
7882 * control v1.0 inserts CRLF whereas for higher versions it only
7883 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7884 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7885 * does for higher. The same test is cloned in riched32 and riched20.
7887 static void test_enter(void)
7889 static const struct {
7890 const char *initialtext;
7891 const int cursor;
7892 const char *expectedwmtext;
7893 const char *expectedemtext;
7894 const char *expectedemtextcrlf;
7895 } testenteritems[] = {
7896 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7897 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7898 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7899 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7900 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7903 char expectedbuf[1024];
7904 char resultbuf[1024];
7905 HWND hwndRichEdit = new_richedit(NULL);
7906 UINT i,j;
7908 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7910 char buf[1024] = {0};
7911 LRESULT result;
7912 GETTEXTEX getText;
7913 const char *expected;
7915 /* Set the text to the initial text */
7916 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
7917 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7919 /* Send Enter */
7920 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7921 simulate_typing_characters(hwndRichEdit, "\r");
7923 /* 1. Retrieve with WM_GETTEXT */
7924 buf[0] = 0x00;
7925 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
7926 expected = testenteritems[i].expectedwmtext;
7928 resultbuf[0]=0x00;
7929 for (j = 0; j < (UINT)result; j++)
7930 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7931 expectedbuf[0] = '\0';
7932 for (j = 0; j < strlen(expected); j++)
7933 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7935 result = strcmp(expected, buf);
7936 ok (result == 0,
7937 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7938 i, resultbuf, expectedbuf);
7940 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7941 getText.cb = sizeof(buf);
7942 getText.flags = GT_DEFAULT;
7943 getText.codepage = CP_ACP;
7944 getText.lpDefaultChar = NULL;
7945 getText.lpUsedDefChar = NULL;
7946 buf[0] = 0x00;
7947 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7948 expected = testenteritems[i].expectedemtext;
7950 resultbuf[0]=0x00;
7951 for (j = 0; j < (UINT)result; j++)
7952 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7953 expectedbuf[0] = '\0';
7954 for (j = 0; j < strlen(expected); j++)
7955 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7957 result = strcmp(expected, buf);
7958 ok (result == 0,
7959 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7960 i, resultbuf, expectedbuf);
7962 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7963 getText.cb = sizeof(buf);
7964 getText.flags = GT_USECRLF;
7965 getText.codepage = CP_ACP;
7966 getText.lpDefaultChar = NULL;
7967 getText.lpUsedDefChar = NULL;
7968 buf[0] = 0x00;
7969 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7970 expected = testenteritems[i].expectedemtextcrlf;
7972 resultbuf[0]=0x00;
7973 for (j = 0; j < (UINT)result; j++)
7974 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7975 expectedbuf[0] = '\0';
7976 for (j = 0; j < strlen(expected); j++)
7977 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7979 result = strcmp(expected, buf);
7980 ok (result == 0,
7981 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7982 i, resultbuf, expectedbuf);
7985 DestroyWindow(hwndRichEdit);
7988 static void test_WM_CREATE(void)
7990 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
7991 static const char title[] = "line1\nline2";
7993 HWND rich_edit;
7994 LRESULT res;
7995 char buf[64];
7996 int len;
7998 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
7999 0, 0, 200, 80, NULL, NULL, NULL, NULL);
8000 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
8002 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8003 ok(len == 5, "GetWindowText returned %d\n", len);
8004 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
8006 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8007 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8009 DestroyWindow(rich_edit);
8011 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
8012 0, 0, 200, 80, NULL, NULL, NULL, NULL);
8013 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
8015 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8016 ok(len == 12, "GetWindowText returned %d\n", len);
8017 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
8019 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8020 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8022 DestroyWindow(rich_edit);
8025 /*******************************************************************
8026 * Test that after deleting all of the text, the first paragraph
8027 * format reverts to the default.
8029 static void test_reset_default_para_fmt( void )
8031 HWND richedit = new_richeditW( NULL );
8032 PARAFORMAT2 fmt;
8033 WORD def_align, new_align;
8035 memset( &fmt, 0, sizeof(fmt) );
8036 fmt.cbSize = sizeof(PARAFORMAT2);
8037 fmt.dwMask = -1;
8038 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8039 def_align = fmt.wAlignment;
8040 new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8042 simulate_typing_characters( richedit, "123" );
8044 SendMessageA( richedit, EM_SETSEL, 0, -1 );
8045 fmt.dwMask = PFM_ALIGNMENT;
8046 fmt.wAlignment = new_align;
8047 SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
8049 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8050 ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
8052 SendMessageA( richedit, EM_SETSEL, 0, -1 );
8053 SendMessageA( richedit, WM_CUT, 0, 0 );
8055 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8056 ok( fmt.wAlignment == def_align, "got %d exppect %d\n", fmt.wAlignment, def_align );
8058 DestroyWindow( richedit );
8061 static void test_EM_SETREADONLY(void)
8063 HWND richedit = new_richeditW(NULL);
8064 DWORD dwStyle;
8065 LRESULT res;
8067 res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
8068 ok(res == 1, "EM_SETREADONLY\n");
8069 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8070 ok(dwStyle & ES_READONLY, "got wrong value: 0x%x\n", dwStyle);
8072 res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
8073 ok(res == 1, "EM_SETREADONLY\n");
8074 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8075 ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%x\n", dwStyle);
8077 DestroyWindow(richedit);
8080 static inline LONG twips2points(LONG value)
8082 return value / 20;
8085 #define TEST_EM_SETFONTSIZE(hwnd,size,expected_size,expected_res,expected_undo) \
8086 _test_font_size(__LINE__,hwnd,size,expected_size,expected_res,expected_undo)
8087 static void _test_font_size(unsigned line, HWND hwnd, LONG size, LONG expected_size,
8088 LRESULT expected_res, BOOL expected_undo)
8090 CHARFORMAT2A cf;
8091 LRESULT res;
8092 BOOL isundo;
8094 cf.cbSize = sizeof(cf);
8095 cf.dwMask = CFM_SIZE;
8097 res = SendMessageA(hwnd, EM_SETFONTSIZE, size, 0);
8098 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
8099 isundo = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
8100 ok_(__FILE__,line)(res == expected_res, "EM_SETFONTSIZE unexpected return value: %lx.\n", res);
8101 ok_(__FILE__,line)(twips2points(cf.yHeight) == expected_size, "got wrong font size: %d, expected: %d\n",
8102 twips2points(cf.yHeight), expected_size);
8103 ok_(__FILE__,line)(isundo == expected_undo, "get wrong undo mark: %d, expected: %d.\n",
8104 isundo, expected_undo);
8107 static void test_EM_SETFONTSIZE(void)
8109 HWND richedit = new_richedit(NULL);
8110 CHAR text[] = "wine";
8111 CHARFORMAT2A tmp_cf;
8112 LONG default_size;
8114 tmp_cf.cbSize = sizeof(tmp_cf);
8115 tmp_cf.dwMask = CFM_SIZE;
8116 tmp_cf.yHeight = 9 * 20.0;
8117 SendMessageA(richedit, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8119 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)text);
8121 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8122 /* without selection */
8123 TEST_EM_SETFONTSIZE(richedit, 1, 10, TRUE, FALSE); /* 9 + 1 -> 10 */
8124 SendMessageA(richedit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8125 default_size = twips2points(tmp_cf.yHeight);
8126 ok(default_size == 9, "Default font size should not be changed.\n");
8127 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8129 SendMessageA(richedit, EM_SETSEL, 0, 2);
8131 TEST_EM_SETFONTSIZE(richedit, 0, 9, TRUE, TRUE); /* 9 + 0 -> 9 */
8133 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8134 TEST_EM_SETFONTSIZE(richedit, 3, 12, TRUE, TRUE); /* 9 + 3 -> 12 */
8135 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8137 TEST_EM_SETFONTSIZE(richedit, 1, 14, TRUE, TRUE); /* 12 + 1 + 1 -> 14 */
8138 TEST_EM_SETFONTSIZE(richedit, -1, 12, TRUE, TRUE); /* 14 - 1 - 1 -> 12 */
8139 TEST_EM_SETFONTSIZE(richedit, 4, 16, TRUE, TRUE); /* 12 + 4 -> 16 */
8140 TEST_EM_SETFONTSIZE(richedit, 3, 20, TRUE, TRUE); /* 16 + 3 + 1 -> 20 */
8141 TEST_EM_SETFONTSIZE(richedit, 0, 20, TRUE, TRUE); /* 20 + 0 -> 20 */
8142 TEST_EM_SETFONTSIZE(richedit, 8, 28, TRUE, TRUE); /* 20 + 8 -> 28 */
8143 TEST_EM_SETFONTSIZE(richedit, 0, 28, TRUE, TRUE); /* 28 + 0 -> 28 */
8144 TEST_EM_SETFONTSIZE(richedit, 1, 36, TRUE, TRUE); /* 28 + 1 -> 36 */
8145 TEST_EM_SETFONTSIZE(richedit, 0, 36, TRUE, TRUE); /* 36 + 0 -> 36 */
8146 TEST_EM_SETFONTSIZE(richedit, 1, 48, TRUE, TRUE); /* 36 + 1 -> 48 */
8147 TEST_EM_SETFONTSIZE(richedit, 0, 48, TRUE, TRUE); /* 48 + 0 -> 48 */
8148 TEST_EM_SETFONTSIZE(richedit, 1, 72, TRUE, TRUE); /* 48 + 1 -> 72 */
8149 TEST_EM_SETFONTSIZE(richedit, 0, 72, TRUE, TRUE); /* 72 + 0 -> 72 */
8150 TEST_EM_SETFONTSIZE(richedit, 1, 80, TRUE, TRUE); /* 72 + 1 -> 80 */
8151 TEST_EM_SETFONTSIZE(richedit, 0, 80, TRUE, TRUE); /* 80 + 0 -> 80 */
8152 TEST_EM_SETFONTSIZE(richedit, 1, 90, TRUE, TRUE); /* 80 + 1 -> 90 */
8153 TEST_EM_SETFONTSIZE(richedit, 0, 90, TRUE, TRUE); /* 90 + 0 -> 90 */
8154 TEST_EM_SETFONTSIZE(richedit, 1, 100, TRUE, TRUE); /* 90 + 1 -> 100 */
8155 TEST_EM_SETFONTSIZE(richedit, 25, 130, TRUE, TRUE); /* 100 + 25 -> 130 */
8156 TEST_EM_SETFONTSIZE(richedit, -1, 120, TRUE, TRUE); /* 130 - 1 -> 120 */
8157 TEST_EM_SETFONTSIZE(richedit, -35, 80, TRUE, TRUE); /* 120 - 35 -> 80 */
8158 TEST_EM_SETFONTSIZE(richedit, -7, 72, TRUE, TRUE); /* 80 - 7 -> 72 */
8159 TEST_EM_SETFONTSIZE(richedit, -42, 28, TRUE, TRUE); /* 72 - 42 -> 28 */
8160 TEST_EM_SETFONTSIZE(richedit, -16, 12, TRUE, TRUE); /* 28 - 16 -> 12 */
8161 TEST_EM_SETFONTSIZE(richedit, -3, 9, TRUE, TRUE); /* 12 - 3 -> 9 */
8162 TEST_EM_SETFONTSIZE(richedit, -8, 1, TRUE, TRUE); /* 9 - 8 -> 1 */
8163 TEST_EM_SETFONTSIZE(richedit, -111, 1, TRUE, TRUE); /* 1 - 111 -> 1 */
8164 TEST_EM_SETFONTSIZE(richedit, 10086, 1638, TRUE, TRUE); /* 1 + 10086 -> 1638 */
8166 /* return FALSE when richedit is TM_PLAINTEXT mode */
8167 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)"");
8168 SendMessageA(richedit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
8169 TEST_EM_SETFONTSIZE(richedit, 0, 9, FALSE, FALSE);
8171 DestroyWindow(richedit);
8174 static void test_alignment_style(void)
8176 HWND richedit = NULL;
8177 PARAFORMAT2 pf;
8178 DWORD align_style[] = {ES_LEFT, ES_CENTER, ES_RIGHT, ES_RIGHT | ES_CENTER,
8179 ES_LEFT | ES_CENTER, ES_LEFT | ES_RIGHT,
8180 ES_LEFT | ES_RIGHT | ES_CENTER};
8181 DWORD align_mask[] = {PFA_LEFT, PFA_CENTER, PFA_RIGHT, PFA_CENTER, PFA_CENTER,
8182 PFA_RIGHT, PFA_CENTER};
8183 const char * streamtext =
8184 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
8185 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
8186 "}\r\n";
8187 EDITSTREAM es;
8188 int i;
8190 for (i = 0; i < sizeof(align_style) / sizeof(align_style[0]); i++)
8192 DWORD dwStyle, new_align;
8194 richedit = new_windowW(RICHEDIT_CLASS20W, align_style[i], NULL);
8195 memset(&pf, 0, sizeof(pf));
8196 pf.cbSize = sizeof(PARAFORMAT2);
8197 pf.dwMask = -1;
8199 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8200 ok(pf.wAlignment == align_mask[i], "(i = %d) got %d expected %d\n",
8201 i, pf.wAlignment, align_mask[i]);
8202 dwStyle = GetWindowLongW(richedit, GWL_STYLE);
8203 ok((i ? (dwStyle & align_style[i]) : (!(dwStyle & 0x0000000f))) ,
8204 "(i = %d) didn't set right align style: 0x%x\n", i, dwStyle);
8207 /* Based on test_reset_default_para_fmt() */
8208 new_align = (align_mask[i] == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8209 simulate_typing_characters(richedit, "123");
8211 SendMessageW(richedit, EM_SETSEL, 0, -1);
8212 pf.dwMask = PFM_ALIGNMENT;
8213 pf.wAlignment = new_align;
8214 SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8216 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8217 ok(pf.wAlignment == new_align, "got %d expect %d\n", pf.wAlignment, new_align);
8219 SendMessageW(richedit, EM_SETSEL, 0, -1);
8220 SendMessageW(richedit, WM_CUT, 0, 0);
8222 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8223 ok(pf.wAlignment == align_mask[i], "got %d exppect %d\n", pf.wAlignment, align_mask[i]);
8225 DestroyWindow(richedit);
8228 /* test with EM_STREAMIN */
8229 richedit = new_windowW(RICHEDIT_CLASS20W, ES_CENTER, NULL);
8230 simulate_typing_characters(richedit, "abc");
8231 es.dwCookie = (DWORD_PTR)&streamtext;
8232 es.dwError = 0;
8233 es.pfnCallback = test_EM_STREAMIN_esCallback;
8234 SendMessageW(richedit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
8235 SendMessageW(richedit, EM_SETSEL, 0, -1);
8236 memset(&pf, 0, sizeof(pf));
8237 pf.cbSize = sizeof(PARAFORMAT2);
8238 pf.dwMask = -1;
8239 SendMessageW(richedit, EM_GETPARAFORMAT, SCF_SELECTION, (LPARAM)&pf);
8240 ok(pf.wAlignment == ES_CENTER, "got %d expected ES_CENTER\n", pf.wAlignment);
8241 DestroyWindow(richedit);
8244 START_TEST( editor )
8246 BOOL ret;
8247 /* Must explicitly LoadLibrary(). The test has no references to functions in
8248 * RICHED20.DLL, so the linker doesn't actually link to it. */
8249 hmoduleRichEdit = LoadLibraryA("riched20.dll");
8250 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
8251 is_lang_japanese = (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE);
8253 test_WM_CHAR();
8254 test_EM_FINDTEXT(FALSE);
8255 test_EM_FINDTEXT(TRUE);
8256 test_EM_GETLINE();
8257 test_EM_POSFROMCHAR();
8258 test_EM_SCROLLCARET();
8259 test_EM_SCROLL();
8260 test_scrollbar_visibility();
8261 test_WM_SETTEXT();
8262 test_EM_LINELENGTH();
8263 test_EM_SETCHARFORMAT();
8264 test_EM_SETTEXTMODE();
8265 test_TM_PLAINTEXT();
8266 test_EM_SETOPTIONS();
8267 test_WM_GETTEXT();
8268 test_EM_GETTEXTRANGE();
8269 test_EM_GETSELTEXT();
8270 test_EM_SETUNDOLIMIT();
8271 test_ES_PASSWORD();
8272 test_EM_SETTEXTEX();
8273 test_EM_LIMITTEXT();
8274 test_EM_EXLIMITTEXT();
8275 test_EM_GETLIMITTEXT();
8276 test_WM_SETFONT();
8277 test_EM_GETMODIFY();
8278 test_EM_SETSEL();
8279 test_EM_EXSETSEL();
8280 test_WM_PASTE();
8281 test_EM_STREAMIN();
8282 test_EM_STREAMOUT();
8283 test_EM_STREAMOUT_FONTTBL();
8284 test_EM_StreamIn_Undo();
8285 test_EM_FORMATRANGE();
8286 test_unicode_conversions();
8287 test_EM_GETTEXTLENGTHEX();
8288 test_EM_REPLACESEL(1);
8289 test_EM_REPLACESEL(0);
8290 test_WM_NOTIFY();
8291 test_EN_LINK();
8292 test_EM_AUTOURLDETECT();
8293 test_eventMask();
8294 test_undo_coalescing();
8295 test_word_movement();
8296 test_EM_CHARFROMPOS();
8297 test_SETPARAFORMAT();
8298 test_word_wrap();
8299 test_autoscroll();
8300 test_format_rect();
8301 test_WM_GETDLGCODE();
8302 test_zoom();
8303 test_dialogmode();
8304 test_EM_FINDWORDBREAK_W();
8305 test_EM_FINDWORDBREAK_A();
8306 test_enter();
8307 test_WM_CREATE();
8308 test_reset_default_para_fmt();
8309 test_EM_SETREADONLY();
8310 test_EM_SETFONTSIZE();
8311 test_alignment_style();
8313 /* Set the environment variable WINETEST_RICHED20 to keep windows
8314 * responsive and open for 30 seconds. This is useful for debugging.
8316 if (getenv( "WINETEST_RICHED20" )) {
8317 keep_responsive(30);
8320 OleFlushClipboard();
8321 ret = FreeLibrary(hmoduleRichEdit);
8322 ok(ret, "error: %d\n", (int) GetLastError());