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