riched20: Consider adjacent runs when computing EN_LINK range.
[wine.git] / dlls / riched20 / tests / editor.c
blob5421614d9a16ac54e014e6c218a88cdf9406663d
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}, tmp[16];
5075 CHARRANGE range;
5077 const char * streamText0 = "{\\rtf1 TestSomeText}";
5078 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5079 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5080 const char * ptr;
5082 const char * streamText1 =
5083 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5084 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5085 "}\r\n";
5087 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5088 const char * streamText2 =
5089 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5090 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5091 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5092 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5093 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5094 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5095 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5097 const char * streamText3 = "RichEdit1";
5099 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5101 const char * streamText4 =
5102 "This text just needs to be long enough to cause run to be split onto "
5103 "two separate lines and make sure the null terminating character is "
5104 "handled properly.\0";
5106 const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5108 int length4 = strlen(streamText4) + 1;
5109 struct StringWithLength cookieForStream4 = {
5110 length4,
5111 (char *)streamText4,
5114 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5115 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5116 struct StringWithLength cookieForStream5 = {
5117 sizeof(streamText5),
5118 (char *)streamText5,
5121 /* Minimal test without \par at the end */
5122 es.dwCookie = (DWORD_PTR)&streamText0;
5123 es.dwError = 0;
5124 es.pfnCallback = test_EM_STREAMIN_esCallback;
5125 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5126 ok(result == 12, "got %ld, expected %d\n", result, 12);
5128 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5129 ok (result == 12,
5130 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5131 result = strcmp (buffer,"TestSomeText");
5132 ok (result == 0,
5133 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5134 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5136 /* Native richedit 2.0 ignores last \par */
5137 ptr = streamText0a;
5138 es.dwCookie = (DWORD_PTR)&ptr;
5139 es.dwError = 0;
5140 es.pfnCallback = test_EM_STREAMIN_esCallback;
5141 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5142 ok(result == 12, "got %ld, expected %d\n", result, 12);
5144 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5145 ok (result == 12,
5146 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5147 result = strcmp (buffer,"TestSomeText");
5148 ok (result == 0,
5149 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5150 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5152 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5153 es.dwCookie = (DWORD_PTR)&streamText0b;
5154 es.dwError = 0;
5155 es.pfnCallback = test_EM_STREAMIN_esCallback;
5156 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5157 ok(result == 13, "got %ld, expected %d\n", result, 13);
5159 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5160 ok (result == 14,
5161 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5162 result = strcmp (buffer,"TestSomeText\r\n");
5163 ok (result == 0,
5164 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5165 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5167 /* Show that when using SFF_SELECTION the last \par is not ignored. */
5168 ptr = streamText0a;
5169 es.dwCookie = (DWORD_PTR)&ptr;
5170 es.dwError = 0;
5171 es.pfnCallback = test_EM_STREAMIN_esCallback;
5172 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5173 ok(result == 12, "got %ld, expected %d\n", result, 12);
5175 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5176 ok (result == 12,
5177 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5178 result = strcmp (buffer,"TestSomeText");
5179 ok (result == 0,
5180 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5181 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5183 range.cpMin = 0;
5184 range.cpMax = -1;
5185 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5186 ok (result == 13, "got %ld\n", result);
5188 ptr = streamText0a;
5189 es.dwCookie = (DWORD_PTR)&ptr;
5190 es.dwError = 0;
5191 es.pfnCallback = test_EM_STREAMIN_esCallback;
5193 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5194 ok(result == 13, "got %ld, expected 13\n", result);
5196 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5197 ok (result == 14,
5198 "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5199 result = strcmp (buffer,"TestSomeText\r\n");
5200 ok (result == 0,
5201 "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5202 ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5204 es.dwCookie = (DWORD_PTR)&streamText1;
5205 es.dwError = 0;
5206 es.pfnCallback = test_EM_STREAMIN_esCallback;
5207 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5208 ok(result == 12, "got %ld, expected %d\n", result, 12);
5210 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5211 ok (result == 12,
5212 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5213 result = strcmp (buffer,"TestSomeText");
5214 ok (result == 0,
5215 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5216 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5218 es.dwCookie = (DWORD_PTR)&streamText2;
5219 es.dwError = 0;
5220 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5221 ok(result == 0, "got %ld, expected %d\n", result, 0);
5223 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5224 ok (result == 0,
5225 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5226 ok (strlen(buffer) == 0,
5227 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5228 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5230 es.dwCookie = (DWORD_PTR)&streamText3;
5231 es.dwError = 0;
5232 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5233 ok(result == 0, "got %ld, expected %d\n", result, 0);
5235 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5236 ok (result == 0,
5237 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5238 ok (strlen(buffer) == 0,
5239 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5240 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5242 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5243 es.dwError = 0;
5244 es.pfnCallback = test_EM_STREAMIN_esCallback;
5245 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5246 ok(result == 18, "got %ld, expected %d\n", result, 18);
5248 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5249 ok(result == 15,
5250 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5251 result = strcmp (buffer,"TestUTF8WithBOM");
5252 ok(result == 0,
5253 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5254 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5256 phase = 0;
5257 es.dwCookie = (DWORD_PTR)&phase;
5258 es.dwError = 0;
5259 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5260 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5261 ok(result == 8, "got %ld\n", result);
5263 WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
5265 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5266 ok(result == 3,
5267 "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
5268 result = memcmp (buffer, tmp, 3);
5269 ok(result == 0,
5270 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
5271 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
5273 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5274 es.dwError = 0;
5275 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5276 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5277 ok(result == length4, "got %ld, expected %d\n", result, length4);
5279 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5280 ok (result == length4,
5281 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5282 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5284 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5285 es.dwError = 0;
5286 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5287 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5288 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5290 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5291 ok (result == length5,
5292 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5293 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5295 DestroyWindow(hwndRichEdit);
5298 static void test_EM_StreamIn_Undo(void)
5300 /* The purpose of this test is to determine when a EM_StreamIn should be
5301 * undoable. This is important because WM_PASTE currently uses StreamIn and
5302 * pasting should always be undoable but streaming isn't always.
5304 * cases to test:
5305 * StreamIn plain text without SFF_SELECTION.
5306 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5307 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5308 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5309 * Feel free to add tests for other text modes or StreamIn things.
5313 HWND hwndRichEdit = new_richedit(NULL);
5314 LRESULT result;
5315 EDITSTREAM es;
5316 char buffer[1024] = {0};
5317 const char randomtext[] = "Some text";
5319 es.pfnCallback = EditStreamCallback;
5321 /* StreamIn, no SFF_SELECTION */
5322 es.dwCookie = nCallbackCount;
5323 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5324 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5325 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5326 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5327 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5328 result = strcmp (buffer,"test");
5329 ok (result == 0,
5330 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5332 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5333 ok (result == FALSE,
5334 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5336 /* StreamIn, SFF_SELECTION, but nothing selected */
5337 es.dwCookie = nCallbackCount;
5338 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5339 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5340 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5341 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5342 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5343 result = strcmp (buffer,"testSome text");
5344 ok (result == 0,
5345 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5347 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5348 ok (result == TRUE,
5349 "EM_STREAMIN with SFF_SELECTION but no selection set "
5350 "should create an undo\n");
5352 /* StreamIn, SFF_SELECTION, with a selection */
5353 es.dwCookie = nCallbackCount;
5354 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5355 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5356 SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
5357 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5358 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5359 result = strcmp (buffer,"Sometesttext");
5360 ok (result == 0,
5361 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5363 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5364 ok (result == TRUE,
5365 "EM_STREAMIN with SFF_SELECTION and selection set "
5366 "should create an undo\n");
5368 DestroyWindow(hwndRichEdit);
5371 static BOOL is_em_settextex_supported(HWND hwnd)
5373 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5374 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5377 static void test_unicode_conversions(void)
5379 static const WCHAR tW[] = {'t',0};
5380 static const WCHAR teW[] = {'t','e',0};
5381 static const WCHAR textW[] = {'t','e','s','t',0};
5382 static const char textA[] = "test";
5383 char bufA[64];
5384 WCHAR bufW[64];
5385 HWND hwnd;
5386 int em_settextex_supported, ret;
5388 #define set_textA(hwnd, wm_set_text, txt) \
5389 do { \
5390 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5391 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5392 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5393 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5394 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5395 } while(0)
5396 #define expect_textA(hwnd, wm_get_text, txt) \
5397 do { \
5398 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5399 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5400 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5401 memset(bufA, 0xAA, sizeof(bufA)); \
5402 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5403 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5404 ret = lstrcmpA(bufA, txt); \
5405 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5406 } while(0)
5408 #define set_textW(hwnd, wm_set_text, txt) \
5409 do { \
5410 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5411 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5412 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5413 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5414 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5415 } while(0)
5416 #define expect_textW(hwnd, wm_get_text, txt) \
5417 do { \
5418 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5419 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5420 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5421 memset(bufW, 0xAA, sizeof(bufW)); \
5422 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5423 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5424 ret = lstrcmpW(bufW, txt); \
5425 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5426 } while(0)
5427 #define expect_empty(hwnd, wm_get_text) \
5428 do { \
5429 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5430 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5431 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5432 memset(bufA, 0xAA, sizeof(bufA)); \
5433 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5434 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5435 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5436 } while(0)
5438 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5439 0, 0, 200, 60, 0, 0, 0, 0);
5440 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5442 ret = IsWindowUnicode(hwnd);
5443 ok(ret, "RichEdit20W should be unicode under NT\n");
5445 /* EM_SETTEXTEX is supported starting from version 3.0 */
5446 em_settextex_supported = is_em_settextex_supported(hwnd);
5447 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5448 em_settextex_supported ? "" : "NOT ");
5450 expect_empty(hwnd, WM_GETTEXT);
5451 expect_empty(hwnd, EM_GETTEXTEX);
5453 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5454 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5455 expect_textA(hwnd, WM_GETTEXT, "t");
5456 expect_textA(hwnd, EM_GETTEXTEX, "t");
5457 expect_textW(hwnd, EM_GETTEXTEX, tW);
5459 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5460 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5461 expect_textA(hwnd, WM_GETTEXT, "te");
5462 expect_textA(hwnd, EM_GETTEXTEX, "te");
5463 expect_textW(hwnd, EM_GETTEXTEX, teW);
5465 set_textA(hwnd, WM_SETTEXT, NULL);
5466 expect_empty(hwnd, WM_GETTEXT);
5467 expect_empty(hwnd, EM_GETTEXTEX);
5469 set_textA(hwnd, WM_SETTEXT, textA);
5470 expect_textA(hwnd, WM_GETTEXT, textA);
5471 expect_textA(hwnd, EM_GETTEXTEX, textA);
5472 expect_textW(hwnd, EM_GETTEXTEX, textW);
5474 if (em_settextex_supported)
5476 set_textA(hwnd, EM_SETTEXTEX, textA);
5477 expect_textA(hwnd, WM_GETTEXT, textA);
5478 expect_textA(hwnd, EM_GETTEXTEX, textA);
5479 expect_textW(hwnd, EM_GETTEXTEX, textW);
5482 set_textW(hwnd, WM_SETTEXT, textW);
5483 expect_textW(hwnd, WM_GETTEXT, textW);
5484 expect_textA(hwnd, WM_GETTEXT, textA);
5485 expect_textW(hwnd, EM_GETTEXTEX, textW);
5486 expect_textA(hwnd, EM_GETTEXTEX, textA);
5488 if (em_settextex_supported)
5490 set_textW(hwnd, EM_SETTEXTEX, textW);
5491 expect_textW(hwnd, WM_GETTEXT, textW);
5492 expect_textA(hwnd, WM_GETTEXT, textA);
5493 expect_textW(hwnd, EM_GETTEXTEX, textW);
5494 expect_textA(hwnd, EM_GETTEXTEX, textA);
5496 DestroyWindow(hwnd);
5498 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5499 0, 0, 200, 60, 0, 0, 0, 0);
5500 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5502 ret = IsWindowUnicode(hwnd);
5503 ok(!ret, "RichEdit20A should NOT be unicode\n");
5505 set_textA(hwnd, WM_SETTEXT, textA);
5506 expect_textA(hwnd, WM_GETTEXT, textA);
5507 expect_textA(hwnd, EM_GETTEXTEX, textA);
5508 expect_textW(hwnd, EM_GETTEXTEX, textW);
5510 if (em_settextex_supported)
5512 set_textA(hwnd, EM_SETTEXTEX, textA);
5513 expect_textA(hwnd, WM_GETTEXT, textA);
5514 expect_textA(hwnd, EM_GETTEXTEX, textA);
5515 expect_textW(hwnd, EM_GETTEXTEX, textW);
5518 set_textW(hwnd, WM_SETTEXT, textW);
5519 expect_textW(hwnd, WM_GETTEXT, textW);
5520 expect_textA(hwnd, WM_GETTEXT, textA);
5521 expect_textW(hwnd, EM_GETTEXTEX, textW);
5522 expect_textA(hwnd, EM_GETTEXTEX, textA);
5524 if (em_settextex_supported)
5526 set_textW(hwnd, EM_SETTEXTEX, textW);
5527 expect_textW(hwnd, WM_GETTEXT, textW);
5528 expect_textA(hwnd, WM_GETTEXT, textA);
5529 expect_textW(hwnd, EM_GETTEXTEX, textW);
5530 expect_textA(hwnd, EM_GETTEXTEX, textA);
5532 DestroyWindow(hwnd);
5535 static void test_WM_CHAR(void)
5537 HWND hwnd;
5538 int ret;
5539 const char * char_list = "abc\rabc\r";
5540 const char * expected_content_single = "abcabc";
5541 const char * expected_content_multi = "abc\r\nabc\r\n";
5542 char buffer[64] = {0};
5543 const char * p;
5545 /* single-line control must IGNORE carriage returns */
5546 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5547 0, 0, 200, 60, 0, 0, 0, 0);
5548 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5550 p = char_list;
5551 while (*p != '\0') {
5552 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5553 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5554 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5555 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5556 p++;
5559 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5560 ret = strcmp(buffer, expected_content_single);
5561 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5563 DestroyWindow(hwnd);
5565 /* multi-line control inserts CR normally */
5566 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5567 0, 0, 200, 60, 0, 0, 0, 0);
5568 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5570 p = char_list;
5571 while (*p != '\0') {
5572 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5573 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5574 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5575 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5576 p++;
5579 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5580 ret = strcmp(buffer, expected_content_multi);
5581 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5583 DestroyWindow(hwnd);
5586 static void test_EM_GETTEXTLENGTHEX(void)
5588 HWND hwnd;
5589 GETTEXTLENGTHEX gtl;
5590 int ret;
5591 const char * base_string = "base string";
5592 const char * test_string = "a\nb\n\n\r\n";
5593 const char * test_string_after = "a";
5594 const char * test_string_2 = "a\rtest\rstring";
5595 char buffer[64] = {0};
5597 /* single line */
5598 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
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);
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 == 1, "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 == 1, "ret %d\n",ret);
5636 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5637 ret = strcmp(buffer, test_string_after);
5638 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5640 DestroyWindow(hwnd);
5642 /* multi line */
5643 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5644 0, 0, 200, 60, 0, 0, 0, 0);
5645 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5647 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5648 gtl.codepage = CP_ACP;
5649 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5650 ok(ret == 0, "ret %d\n",ret);
5652 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5653 gtl.codepage = CP_ACP;
5654 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5655 ok(ret == 0, "ret %d\n",ret);
5657 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5659 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5660 gtl.codepage = CP_ACP;
5661 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5662 ok(ret == strlen(base_string), "ret %d\n",ret);
5664 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5665 gtl.codepage = CP_ACP;
5666 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5667 ok(ret == strlen(base_string), "ret %d\n",ret);
5669 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5671 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5672 gtl.codepage = CP_ACP;
5673 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5674 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5676 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5677 gtl.codepage = CP_ACP;
5678 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5679 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5681 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5683 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5684 gtl.codepage = CP_ACP;
5685 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5686 ok(ret == 10, "ret %d\n",ret);
5688 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5689 gtl.codepage = CP_ACP;
5690 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5691 ok(ret == 6, "ret %d\n",ret);
5693 /* Unicode/NUMCHARS/NUMBYTES */
5694 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5696 gtl.flags = GTL_DEFAULT;
5697 gtl.codepage = 1200;
5698 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5699 ok(ret == lstrlenA(test_string_2),
5700 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5702 gtl.flags = GTL_NUMCHARS;
5703 gtl.codepage = 1200;
5704 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5705 ok(ret == lstrlenA(test_string_2),
5706 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5708 gtl.flags = GTL_NUMBYTES;
5709 gtl.codepage = 1200;
5710 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5711 ok(ret == lstrlenA(test_string_2)*2,
5712 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5714 gtl.flags = GTL_PRECISE;
5715 gtl.codepage = 1200;
5716 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5717 ok(ret == lstrlenA(test_string_2)*2,
5718 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5720 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5721 gtl.codepage = 1200;
5722 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5723 ok(ret == lstrlenA(test_string_2),
5724 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5726 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5727 gtl.codepage = 1200;
5728 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5729 ok(ret == E_INVALIDARG,
5730 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5732 DestroyWindow(hwnd);
5736 /* globals that parent and child access when checking event masks & notifications */
5737 static HWND eventMaskEditHwnd = 0;
5738 static int queriedEventMask;
5739 static int watchForEventMask = 0;
5741 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5742 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5744 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5746 queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5748 return DefWindowProcA(hwnd, message, wParam, lParam);
5751 /* test event masks in combination with WM_COMMAND */
5752 static void test_eventMask(void)
5754 HWND parent;
5755 int ret, style;
5756 WNDCLASSA cls;
5757 const char text[] = "foo bar\n";
5758 int eventMask;
5760 /* register class to capture WM_COMMAND */
5761 cls.style = 0;
5762 cls.lpfnWndProc = ParentMsgCheckProcA;
5763 cls.cbClsExtra = 0;
5764 cls.cbWndExtra = 0;
5765 cls.hInstance = GetModuleHandleA(0);
5766 cls.hIcon = 0;
5767 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5768 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5769 cls.lpszMenuName = NULL;
5770 cls.lpszClassName = "EventMaskParentClass";
5771 if(!RegisterClassA(&cls)) assert(0);
5773 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5774 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5775 ok (parent != 0, "Failed to create parent window\n");
5777 eventMaskEditHwnd = new_richedit(parent);
5778 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5780 eventMask = ENM_CHANGE | ENM_UPDATE;
5781 ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5782 ok(ret == ENM_NONE, "wrong event mask\n");
5783 ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5784 ok(ret == eventMask, "failed to set event mask\n");
5786 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5787 queriedEventMask = 0; /* initialize to something other than we expect */
5788 watchForEventMask = EN_CHANGE;
5789 ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
5790 ok(ret == TRUE, "failed to set text\n");
5791 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5792 notification in response to WM_SETTEXT */
5793 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5794 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5796 /* check to see if EN_CHANGE is sent when redraw is turned off */
5797 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5798 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5799 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5800 /* redraw is disabled by making the window invisible. */
5801 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5802 queriedEventMask = 0; /* initialize to something other than we expect */
5803 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5804 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5805 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5806 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5807 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5809 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5810 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5811 style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
5812 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5813 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5814 watchForEventMask = EN_UPDATE;
5815 queriedEventMask = 0; /* initialize to something other than we expect */
5816 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5817 ok(queriedEventMask == 0,
5818 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5819 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
5820 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5821 queriedEventMask = 0; /* initialize to something other than we expect */
5822 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5823 ok(queriedEventMask == eventMask,
5824 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5827 DestroyWindow(parent);
5830 static int received_WM_NOTIFY = 0;
5831 static int modify_at_WM_NOTIFY = 0;
5832 static BOOL filter_on_WM_NOTIFY = FALSE;
5833 static HWND hwndRichedit_WM_NOTIFY;
5835 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5837 if(message == WM_NOTIFY)
5839 received_WM_NOTIFY = 1;
5840 modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5841 if (filter_on_WM_NOTIFY) return TRUE;
5843 return DefWindowProcA(hwnd, message, wParam, lParam);
5846 static void test_WM_NOTIFY(void)
5848 HWND parent;
5849 WNDCLASSA cls;
5850 CHARFORMAT2A cf2;
5851 int sel_start, sel_end;
5853 /* register class to capture WM_NOTIFY */
5854 cls.style = 0;
5855 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5856 cls.cbClsExtra = 0;
5857 cls.cbWndExtra = 0;
5858 cls.hInstance = GetModuleHandleA(0);
5859 cls.hIcon = 0;
5860 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5861 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5862 cls.lpszMenuName = NULL;
5863 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5864 if(!RegisterClassA(&cls)) assert(0);
5866 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5867 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5868 ok (parent != 0, "Failed to create parent window\n");
5870 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5871 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5873 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5875 /* Notifications for selection change should only be sent when selection
5876 actually changes. EM_SETCHARFORMAT is one message that calls
5877 ME_CommitUndo, which should check whether message should be sent */
5878 received_WM_NOTIFY = 0;
5879 cf2.cbSize = sizeof(CHARFORMAT2A);
5880 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5881 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5882 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5883 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
5884 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5886 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5887 already at 0. */
5888 received_WM_NOTIFY = 0;
5889 modify_at_WM_NOTIFY = 0;
5890 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5891 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5892 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5894 received_WM_NOTIFY = 0;
5895 modify_at_WM_NOTIFY = 0;
5896 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5897 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5899 received_WM_NOTIFY = 0;
5900 modify_at_WM_NOTIFY = 0;
5901 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5902 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5903 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5905 /* Test for WM_NOTIFY messages with redraw disabled. */
5906 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5907 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5908 received_WM_NOTIFY = 0;
5909 SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5910 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5911 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5913 /* Test filtering key events. */
5914 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5915 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5916 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5917 received_WM_NOTIFY = 0;
5918 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5919 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5920 ok(sel_start == 1 && sel_end == 1,
5921 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5922 filter_on_WM_NOTIFY = TRUE;
5923 received_WM_NOTIFY = 0;
5924 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5925 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5926 ok(sel_start == 1 && sel_end == 1,
5927 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5929 /* test with owner set to NULL */
5930 SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5931 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5932 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5933 ok(sel_start == 1 && sel_end == 1,
5934 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5936 DestroyWindow(hwndRichedit_WM_NOTIFY);
5937 DestroyWindow(parent);
5940 static int cpMin_EN_LINK = -1;
5941 static int cpMax_EN_LINK = -1;
5943 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5945 ENLINK* enlink = (ENLINK*)lParam;
5946 if(message == WM_NOTIFY && enlink->nmhdr.code == EN_LINK)
5948 cpMin_EN_LINK = enlink->chrg.cpMin;
5949 cpMax_EN_LINK = enlink->chrg.cpMax;
5951 return DefWindowProcA(hwnd, message, wParam, lParam);
5954 static void test_EN_LINK(void)
5956 HWND parent;
5957 WNDCLASSA cls;
5958 HWND hwndRichedit_EN_LINK;
5959 CHARFORMAT2A cf2;
5961 /* register class to capture WM_NOTIFY */
5962 cls.style = 0;
5963 cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
5964 cls.cbClsExtra = 0;
5965 cls.cbWndExtra = 0;
5966 cls.hInstance = GetModuleHandleA(0);
5967 cls.hIcon = 0;
5968 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5969 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5970 cls.lpszMenuName = NULL;
5971 cls.lpszClassName = "EN_LINK_ParentClass";
5972 if(!RegisterClassA(&cls)) assert(0);
5974 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5975 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5976 ok(parent != 0, "Failed to create parent window\n");
5978 hwndRichedit_EN_LINK = new_richedit(parent);
5979 ok(hwndRichedit_EN_LINK != 0, "Failed to create edit window\n");
5981 SendMessageA(hwndRichedit_EN_LINK, EM_SETEVENTMASK, 0, ENM_LINK);
5983 cf2.cbSize = sizeof(CHARFORMAT2A);
5984 cf2.dwMask = CFM_LINK;
5985 cf2.dwEffects = CFE_LINK;
5986 SendMessageA(hwndRichedit_EN_LINK, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
5987 /* mixing letters and numbers causes runs to be split */
5988 SendMessageA(hwndRichedit_EN_LINK, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
5989 SendMessageA(hwndRichedit_EN_LINK, WM_LBUTTONDOWN, 0, MAKELPARAM(5, 5));
5990 ok(cpMin_EN_LINK == 0 && cpMax_EN_LINK == 31, "Expected link range [0,31) got [%i,%i)\n", cpMin_EN_LINK, cpMax_EN_LINK);
5992 DestroyWindow(hwndRichedit_EN_LINK);
5993 DestroyWindow(parent);
5996 static void test_undo_coalescing(void)
5998 HWND hwnd;
5999 int result;
6000 char buffer[64] = {0};
6002 /* multi-line control inserts CR normally */
6003 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6004 0, 0, 200, 60, 0, 0, 0, 0);
6005 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6007 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6008 ok (result == FALSE, "Can undo after window creation.\n");
6009 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6010 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6011 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6012 ok (result == FALSE, "Can redo after window creation.\n");
6013 result = SendMessageA(hwnd, EM_REDO, 0, 0);
6014 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6016 /* Test the effect of arrows keys during typing on undo transactions*/
6017 simulate_typing_characters(hwnd, "one two three");
6018 SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6019 SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6020 simulate_typing_characters(hwnd, " four five six");
6022 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6023 ok (result == FALSE, "Can redo before anything is undone.\n");
6024 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6025 ok (result == TRUE, "Cannot undo typed characters.\n");
6026 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6027 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6028 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6029 ok (result == TRUE, "Cannot redo after undo.\n");
6030 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6031 result = strcmp(buffer, "one two three");
6032 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6034 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6035 ok (result == TRUE, "Cannot undo typed characters.\n");
6036 result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6037 ok (result == TRUE, "Failed to undo typed characters.\n");
6038 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6039 result = strcmp(buffer, "");
6040 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6042 /* Test the effect of focus changes during typing on undo transactions*/
6043 simulate_typing_characters(hwnd, "one two three");
6044 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6045 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6046 SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6047 SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6048 simulate_typing_characters(hwnd, " four five six");
6049 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6050 ok (result == TRUE, "Failed to undo typed characters.\n");
6051 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6052 result = strcmp(buffer, "one two three");
6053 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6055 /* Test the effect of the back key during typing on undo transactions */
6056 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6057 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6058 ok (result == TRUE, "Failed to clear the text.\n");
6059 simulate_typing_characters(hwnd, "one two threa");
6060 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6061 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6062 SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
6063 SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
6064 simulate_typing_characters(hwnd, "e four five six");
6065 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6066 ok (result == TRUE, "Failed to undo typed characters.\n");
6067 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6068 result = strcmp(buffer, "");
6069 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6071 /* Test the effect of the delete key during typing on undo transactions */
6072 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6073 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
6074 ok(result == TRUE, "Failed to set the text.\n");
6075 SendMessageA(hwnd, EM_SETSEL, 1, 1);
6076 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6077 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6078 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6079 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6080 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6081 ok (result == TRUE, "Failed to undo typed characters.\n");
6082 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6083 result = strcmp(buffer, "acd");
6084 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
6085 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6086 ok (result == TRUE, "Failed to undo typed characters.\n");
6087 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6088 result = strcmp(buffer, "abcd");
6089 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
6091 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
6092 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6093 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6094 ok (result == TRUE, "Failed to clear the text.\n");
6095 simulate_typing_characters(hwnd, "one two three");
6096 result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
6097 ok (result == 0, "expected %d but got %d\n", 0, result);
6098 simulate_typing_characters(hwnd, " four five six");
6099 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6100 ok (result == TRUE, "Failed to undo typed characters.\n");
6101 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6102 result = strcmp(buffer, "one two three");
6103 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6104 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6105 ok (result == TRUE, "Failed to undo typed characters.\n");
6106 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6107 result = strcmp(buffer, "");
6108 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6110 DestroyWindow(hwnd);
6113 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
6115 int length;
6117 /* MSDN lied, length is actually the number of bytes. */
6118 length = bytes / sizeof(WCHAR);
6119 switch(code)
6121 case WB_ISDELIMITER:
6122 return text[pos] == 'X';
6123 case WB_LEFT:
6124 case WB_MOVEWORDLEFT:
6125 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6126 return pos-1;
6127 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
6128 case WB_LEFTBREAK:
6129 pos--;
6130 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6131 pos--;
6132 return pos;
6133 case WB_RIGHT:
6134 case WB_MOVEWORDRIGHT:
6135 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6136 return pos+1;
6137 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
6138 case WB_RIGHTBREAK:
6139 pos++;
6140 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6141 pos++;
6142 return pos;
6143 default:
6144 ok(FALSE, "Unexpected code %d\n", code);
6145 break;
6147 return 0;
6150 static void test_word_movement(void)
6152 HWND hwnd;
6153 int result;
6154 int sel_start, sel_end;
6155 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6157 /* multi-line control inserts CR normally */
6158 hwnd = new_richedit(NULL);
6160 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
6161 ok (result == TRUE, "Failed to clear the text.\n");
6162 SendMessageA(hwnd, EM_SETSEL, 0, 0);
6163 /* |one two three */
6165 send_ctrl_key(hwnd, VK_RIGHT);
6166 /* one |two three */
6167 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6168 ok(sel_start == sel_end, "Selection should be empty\n");
6169 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6171 send_ctrl_key(hwnd, VK_RIGHT);
6172 /* one two |three */
6173 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6174 ok(sel_start == sel_end, "Selection should be empty\n");
6175 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6177 send_ctrl_key(hwnd, VK_LEFT);
6178 /* one |two three */
6179 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6180 ok(sel_start == sel_end, "Selection should be empty\n");
6181 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6183 send_ctrl_key(hwnd, VK_LEFT);
6184 /* |one two three */
6185 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6186 ok(sel_start == sel_end, "Selection should be empty\n");
6187 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6189 SendMessageA(hwnd, EM_SETSEL, 8, 8);
6190 /* one two | three */
6191 send_ctrl_key(hwnd, VK_RIGHT);
6192 /* one two |three */
6193 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6194 ok(sel_start == sel_end, "Selection should be empty\n");
6195 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6197 SendMessageA(hwnd, EM_SETSEL, 11, 11);
6198 /* one two th|ree */
6199 send_ctrl_key(hwnd, VK_LEFT);
6200 /* one two |three */
6201 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6202 ok(sel_start == sel_end, "Selection should be empty\n");
6203 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6205 /* Test with a custom word break procedure that uses X as the delimiter. */
6206 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6207 ok (result == TRUE, "Failed to clear the text.\n");
6208 SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6209 /* |one twoXthree */
6210 send_ctrl_key(hwnd, VK_RIGHT);
6211 /* one twoX|three */
6212 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6213 ok(sel_start == sel_end, "Selection should be empty\n");
6214 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6216 DestroyWindow(hwnd);
6218 /* Make sure the behaviour is the same with a unicode richedit window,
6219 * and using unicode functions. */
6221 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6222 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6223 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6225 /* Test with a custom word break procedure that uses X as the delimiter. */
6226 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6227 ok (result == TRUE, "Failed to clear the text.\n");
6228 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6229 /* |one twoXthree */
6230 send_ctrl_key(hwnd, VK_RIGHT);
6231 /* one twoX|three */
6232 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6233 ok(sel_start == sel_end, "Selection should be empty\n");
6234 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6236 DestroyWindow(hwnd);
6239 static void test_EM_CHARFROMPOS(void)
6241 HWND hwnd;
6242 int result;
6243 RECT rcClient;
6244 POINTL point;
6245 point.x = 0;
6246 point.y = 40;
6248 /* multi-line control inserts CR normally */
6249 hwnd = new_richedit(NULL);
6250 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6251 (LPARAM)"one two three four five six seven\reight");
6252 ok(result == 1, "Expected 1, got %d\n", result);
6253 GetClientRect(hwnd, &rcClient);
6255 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6256 ok(result == 34, "expected character index of 34 but got %d\n", result);
6258 /* Test with points outside the bounds of the richedit control. */
6259 point.x = -1;
6260 point.y = 40;
6261 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6262 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6264 point.x = 1000;
6265 point.y = 0;
6266 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6267 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6269 point.x = 1000;
6270 point.y = 36;
6271 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6272 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6274 point.x = 1000;
6275 point.y = -1;
6276 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6277 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6279 point.x = 1000;
6280 point.y = rcClient.bottom + 1;
6281 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6282 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6284 point.x = 1000;
6285 point.y = rcClient.bottom;
6286 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6287 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6289 DestroyWindow(hwnd);
6292 static void test_word_wrap(void)
6294 HWND hwnd;
6295 POINTL point = {0, 60}; /* This point must be below the first line */
6296 const char *text = "Must be long enough to test line wrapping";
6297 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6298 int res, pos, lines;
6300 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6301 * when specified on window creation and set later. */
6302 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6303 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6304 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6305 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6306 ok(res, "WM_SETTEXT failed.\n");
6307 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6308 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6309 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6310 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6312 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6313 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6314 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6315 DestroyWindow(hwnd);
6317 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
6318 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6319 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6321 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6322 ok(res, "WM_SETTEXT failed.\n");
6323 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6324 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6325 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6326 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6328 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6329 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6330 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6331 DestroyWindow(hwnd);
6333 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6334 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6335 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6336 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6337 ok(res, "WM_SETTEXT failed.\n");
6338 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6339 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6341 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6342 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6343 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6344 DestroyWindow(hwnd);
6346 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
6347 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6348 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6349 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6350 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6351 ok(res, "WM_SETTEXT failed.\n");
6352 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6353 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6355 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6356 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6357 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6359 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6360 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
6361 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6362 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6363 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6365 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
6366 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6367 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6368 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6369 DestroyWindow(hwnd);
6371 /* Test to see if wrapping happens with redraw disabled. */
6372 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6373 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6374 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6375 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
6376 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
6377 ok(res, "EM_REPLACESEL failed.\n");
6378 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6379 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6380 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6381 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6382 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6384 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6385 DestroyWindow(hwnd);
6388 static void test_autoscroll(void)
6390 HWND hwnd = new_richedit(NULL);
6391 int lines, ret, redraw;
6392 POINT pt;
6394 for (redraw = 0; redraw <= 1; redraw++) {
6395 trace("testing with WM_SETREDRAW=%d\n", redraw);
6396 SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
6397 SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6398 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6399 ok(lines == 8, "%d lines instead of 8\n", lines);
6400 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6401 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6402 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6403 ret = GetWindowLongA(hwnd, GWL_STYLE);
6404 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6406 SendMessageA(hwnd, WM_SETTEXT, 0, 0);
6407 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6408 ok(lines == 1, "%d lines instead of 1\n", lines);
6409 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6410 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6411 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6412 ret = GetWindowLongA(hwnd, GWL_STYLE);
6413 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6416 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6417 DestroyWindow(hwnd);
6419 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6420 * auto vertical/horizontal scrolling options. */
6421 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6422 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6423 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6424 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6425 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6426 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6427 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6428 ret = GetWindowLongA(hwnd, GWL_STYLE);
6429 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6430 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6431 DestroyWindow(hwnd);
6433 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6434 WS_POPUP|ES_MULTILINE,
6435 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6436 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6437 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6438 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6439 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6440 ret = GetWindowLongA(hwnd, GWL_STYLE);
6441 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6442 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6443 DestroyWindow(hwnd);
6447 static void test_format_rect(void)
6449 HWND hwnd;
6450 RECT rc, expected, clientRect;
6451 int n;
6452 DWORD options;
6454 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6455 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6456 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6457 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6459 GetClientRect(hwnd, &clientRect);
6461 expected = clientRect;
6462 expected.left += 1;
6463 expected.right -= 1;
6464 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6465 ok(rc.top == expected.top && rc.left == expected.left &&
6466 rc.bottom == expected.bottom && rc.right == expected.right,
6467 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6468 rc.top, rc.left, rc.bottom, rc.right,
6469 expected.top, expected.left, expected.bottom, expected.right);
6471 for (n = -3; n <= 3; n++)
6473 rc = clientRect;
6474 rc.top += n;
6475 rc.left += n;
6476 rc.bottom -= n;
6477 rc.right -= n;
6478 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6480 expected = rc;
6481 expected.top = max(0, rc.top);
6482 expected.left = max(0, rc.left);
6483 expected.bottom = min(clientRect.bottom, rc.bottom);
6484 expected.right = min(clientRect.right, rc.right);
6485 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6486 ok(rc.top == expected.top && rc.left == expected.left &&
6487 rc.bottom == expected.bottom && rc.right == expected.right,
6488 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6489 n, rc.top, rc.left, rc.bottom, rc.right,
6490 expected.top, expected.left, expected.bottom, expected.right);
6493 rc = clientRect;
6494 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6495 expected = clientRect;
6496 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6497 ok(rc.top == expected.top && rc.left == expected.left &&
6498 rc.bottom == expected.bottom && rc.right == expected.right,
6499 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6500 rc.top, rc.left, rc.bottom, rc.right,
6501 expected.top, expected.left, expected.bottom, expected.right);
6503 /* Adding the selectionbar adds the selectionbar width to the left side. */
6504 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6505 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6506 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6507 expected.left += 8; /* selection bar width */
6508 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6509 ok(rc.top == expected.top && rc.left == expected.left &&
6510 rc.bottom == expected.bottom && rc.right == expected.right,
6511 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6512 rc.top, rc.left, rc.bottom, rc.right,
6513 expected.top, expected.left, expected.bottom, expected.right);
6515 rc = clientRect;
6516 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6517 expected = clientRect;
6518 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6519 ok(rc.top == expected.top && rc.left == expected.left &&
6520 rc.bottom == expected.bottom && rc.right == expected.right,
6521 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6522 rc.top, rc.left, rc.bottom, rc.right,
6523 expected.top, expected.left, expected.bottom, expected.right);
6525 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6526 * even if the left side is already 0. */
6527 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6528 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6529 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6530 expected.left -= 8; /* selection bar width */
6531 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6532 ok(rc.top == expected.top && rc.left == expected.left &&
6533 rc.bottom == expected.bottom && rc.right == expected.right,
6534 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6535 rc.top, rc.left, rc.bottom, rc.right,
6536 expected.top, expected.left, expected.bottom, expected.right);
6538 /* Set the absolute value of the formatting rectangle. */
6539 rc = clientRect;
6540 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6541 expected = clientRect;
6542 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6543 ok(rc.top == expected.top && rc.left == expected.left &&
6544 rc.bottom == expected.bottom && rc.right == expected.right,
6545 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6546 n, rc.top, rc.left, rc.bottom, rc.right,
6547 expected.top, expected.left, expected.bottom, expected.right);
6549 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6550 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6551 * tests show that this isn't true. */
6552 rc.top = 15;
6553 rc.left = 15;
6554 rc.bottom = clientRect.bottom - 15;
6555 rc.right = clientRect.right - 15;
6556 expected = rc;
6557 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6558 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6559 ok(rc.top == expected.top && rc.left == expected.left &&
6560 rc.bottom == expected.bottom && rc.right == expected.right,
6561 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6562 rc.top, rc.left, rc.bottom, rc.right,
6563 expected.top, expected.left, expected.bottom, expected.right);
6565 /* For some reason it does not limit the values to the client rect with
6566 * a WPARAM value of 1. */
6567 rc.top = -15;
6568 rc.left = -15;
6569 rc.bottom = clientRect.bottom + 15;
6570 rc.right = clientRect.right + 15;
6571 expected = rc;
6572 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6573 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6574 ok(rc.top == expected.top && rc.left == expected.left &&
6575 rc.bottom == expected.bottom && rc.right == expected.right,
6576 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6577 rc.top, rc.left, rc.bottom, rc.right,
6578 expected.top, expected.left, expected.bottom, expected.right);
6580 /* Reset to default rect and check how the format rect adjusts to window
6581 * resize and how it copes with very small windows */
6582 SendMessageA(hwnd, EM_SETRECT, 0, 0);
6584 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6585 GetClientRect(hwnd, &clientRect);
6587 expected = clientRect;
6588 expected.left += 1;
6589 expected.right -= 1;
6590 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6591 ok(rc.top == expected.top && rc.left == expected.left &&
6592 rc.bottom == expected.bottom && rc.right == expected.right,
6593 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6594 rc.top, rc.left, rc.bottom, rc.right,
6595 expected.top, expected.left, expected.bottom, expected.right);
6597 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6598 GetClientRect(hwnd, &clientRect);
6600 expected = clientRect;
6601 expected.left += 1;
6602 expected.right -= 1;
6603 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6604 ok(rc.top == expected.top && rc.left == expected.left &&
6605 rc.bottom == expected.bottom && rc.right == expected.right,
6606 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6607 rc.top, rc.left, rc.bottom, rc.right,
6608 expected.top, expected.left, expected.bottom, expected.right);
6610 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6611 GetClientRect(hwnd, &clientRect);
6613 expected = clientRect;
6614 expected.left += 1;
6615 expected.right -= 1;
6616 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6617 ok(rc.top == expected.top && rc.left == expected.left &&
6618 rc.bottom == expected.bottom && rc.right == expected.right,
6619 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6620 rc.top, rc.left, rc.bottom, rc.right,
6621 expected.top, expected.left, expected.bottom, expected.right);
6623 DestroyWindow(hwnd);
6625 /* The extended window style affects the formatting rectangle. */
6626 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
6627 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6628 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6629 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6631 GetClientRect(hwnd, &clientRect);
6633 expected = clientRect;
6634 expected.left += 1;
6635 expected.top += 1;
6636 expected.right -= 1;
6637 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6638 ok(rc.top == expected.top && rc.left == expected.left &&
6639 rc.bottom == expected.bottom && rc.right == expected.right,
6640 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6641 rc.top, rc.left, rc.bottom, rc.right,
6642 expected.top, expected.left, expected.bottom, expected.right);
6644 rc = clientRect;
6645 rc.top += 5;
6646 rc.left += 5;
6647 rc.bottom -= 5;
6648 rc.right -= 5;
6649 expected = rc;
6650 expected.top -= 1;
6651 expected.left -= 1;
6652 expected.right += 1;
6653 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6654 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6655 ok(rc.top == expected.top && rc.left == expected.left &&
6656 rc.bottom == expected.bottom && rc.right == expected.right,
6657 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6658 rc.top, rc.left, rc.bottom, rc.right,
6659 expected.top, expected.left, expected.bottom, expected.right);
6661 DestroyWindow(hwnd);
6664 static void test_WM_GETDLGCODE(void)
6666 HWND hwnd;
6667 UINT res, expected;
6668 MSG msg;
6670 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6672 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6673 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6674 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6675 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6676 msg.hwnd = hwnd;
6677 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6678 expected = expected | DLGC_WANTMESSAGE;
6679 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6680 res, expected);
6681 DestroyWindow(hwnd);
6683 msg.message = WM_KEYDOWN;
6684 msg.wParam = VK_RETURN;
6685 msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6686 msg.pt.x = 0;
6687 msg.pt.y = 0;
6688 msg.time = GetTickCount();
6690 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6691 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6692 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6693 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6694 msg.hwnd = hwnd;
6695 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6696 expected = expected | DLGC_WANTMESSAGE;
6697 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6698 res, expected);
6699 DestroyWindow(hwnd);
6701 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6702 ES_MULTILINE|WS_POPUP,
6703 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6704 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6705 msg.hwnd = hwnd;
6706 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6707 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6708 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6709 res, expected);
6710 DestroyWindow(hwnd);
6712 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6713 ES_WANTRETURN|WS_POPUP,
6714 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6715 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6716 msg.hwnd = hwnd;
6717 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6718 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6719 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6720 res, expected);
6721 DestroyWindow(hwnd);
6723 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6724 WS_POPUP,
6725 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6726 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6727 msg.hwnd = hwnd;
6728 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6729 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6730 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6731 res, expected);
6732 DestroyWindow(hwnd);
6734 msg.wParam = VK_TAB;
6735 msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6737 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6738 ES_MULTILINE|WS_POPUP,
6739 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6740 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6741 msg.hwnd = hwnd;
6742 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6743 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6744 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6745 res, expected);
6746 DestroyWindow(hwnd);
6748 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6749 WS_POPUP,
6750 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6751 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6752 msg.hwnd = hwnd;
6753 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6754 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6755 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6756 res, expected);
6757 DestroyWindow(hwnd);
6759 hold_key(VK_CONTROL);
6761 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6762 ES_MULTILINE|WS_POPUP,
6763 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6764 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6765 msg.hwnd = hwnd;
6766 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6767 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6768 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6769 res, expected);
6770 DestroyWindow(hwnd);
6772 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6773 WS_POPUP,
6774 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6775 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6776 msg.hwnd = hwnd;
6777 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6778 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6779 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6780 res, expected);
6781 DestroyWindow(hwnd);
6783 release_key(VK_CONTROL);
6785 msg.wParam = 'a';
6786 msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6788 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6789 ES_MULTILINE|WS_POPUP,
6790 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6791 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6792 msg.hwnd = hwnd;
6793 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6794 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6795 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6796 res, expected);
6797 DestroyWindow(hwnd);
6799 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6800 WS_POPUP,
6801 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6802 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6803 msg.hwnd = hwnd;
6804 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6805 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6806 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6807 res, expected);
6808 DestroyWindow(hwnd);
6810 msg.message = WM_CHAR;
6812 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6813 ES_MULTILINE|WS_POPUP,
6814 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6815 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6816 msg.hwnd = hwnd;
6817 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6818 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6819 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6820 res, expected);
6821 DestroyWindow(hwnd);
6823 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6824 WS_POPUP,
6825 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6826 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6827 msg.hwnd = hwnd;
6828 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6829 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6830 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6831 res, expected);
6832 DestroyWindow(hwnd);
6834 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6835 WS_POPUP|ES_SAVESEL,
6836 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6837 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6838 res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
6839 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
6840 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6841 res, expected);
6842 DestroyWindow(hwnd);
6845 static void test_zoom(void)
6847 HWND hwnd;
6848 UINT ret;
6849 RECT rc;
6850 POINT pt;
6851 int numerator, denominator;
6853 hwnd = new_richedit(NULL);
6854 GetClientRect(hwnd, &rc);
6855 pt.x = (rc.right - rc.left) / 2;
6856 pt.y = (rc.bottom - rc.top) / 2;
6857 ClientToScreen(hwnd, &pt);
6859 /* Test initial zoom value */
6860 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6861 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6862 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6863 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6865 /* test scroll wheel */
6866 hold_key(VK_CONTROL);
6867 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6868 MAKELPARAM(pt.x, pt.y));
6869 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6870 release_key(VK_CONTROL);
6872 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6873 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6874 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6875 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6877 /* Test how much the mouse wheel can zoom in and out. */
6878 ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
6879 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6881 hold_key(VK_CONTROL);
6882 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6883 MAKELPARAM(pt.x, pt.y));
6884 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6885 release_key(VK_CONTROL);
6887 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6888 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6889 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6890 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6892 ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
6893 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6895 hold_key(VK_CONTROL);
6896 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6897 MAKELPARAM(pt.x, pt.y));
6898 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6899 release_key(VK_CONTROL);
6901 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6902 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6903 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6904 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6906 ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
6907 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6909 hold_key(VK_CONTROL);
6910 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6911 MAKELPARAM(pt.x, pt.y));
6912 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6913 release_key(VK_CONTROL);
6915 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6916 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6917 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6918 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6920 ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
6921 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6923 hold_key(VK_CONTROL);
6924 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6925 MAKELPARAM(pt.x, pt.y));
6926 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6927 release_key(VK_CONTROL);
6929 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6930 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6931 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6932 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6934 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6935 ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
6936 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6938 hold_key(VK_CONTROL);
6939 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6940 MAKELPARAM(pt.x, pt.y));
6941 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6942 release_key(VK_CONTROL);
6944 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6945 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6946 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6947 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6949 /* Test bounds checking on EM_SETZOOM */
6950 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
6951 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6953 ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
6954 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6956 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
6957 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6959 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6960 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6961 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6962 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6964 ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
6965 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6967 /* See if negative numbers are accepted. */
6968 ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
6969 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6971 /* See if negative numbers are accepted. */
6972 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
6973 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6975 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6976 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6977 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6978 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6980 /* Reset the zoom value */
6981 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
6982 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6984 DestroyWindow(hwnd);
6987 struct dialog_mode_messages
6989 int wm_getdefid, wm_close, wm_nextdlgctl;
6992 static struct dialog_mode_messages dm_messages;
6994 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6995 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6996 "got %d\n", wmclose, dm_messages.wm_close); \
6997 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6998 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6999 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7000 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7002 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7004 switch (iMsg)
7006 case DM_GETDEFID:
7007 dm_messages.wm_getdefid++;
7008 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7009 case WM_NEXTDLGCTL:
7010 dm_messages.wm_nextdlgctl++;
7011 break;
7012 case WM_CLOSE:
7013 dm_messages.wm_close++;
7014 break;
7017 return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7020 static void test_dialogmode(void)
7022 HWND hwRichEdit, hwParent, hwButton;
7023 MSG msg= {0};
7024 int lcount, r;
7025 WNDCLASSA cls;
7027 cls.style = 0;
7028 cls.lpfnWndProc = dialog_mode_wnd_proc;
7029 cls.cbClsExtra = 0;
7030 cls.cbWndExtra = 0;
7031 cls.hInstance = GetModuleHandleA(0);
7032 cls.hIcon = 0;
7033 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7034 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7035 cls.lpszMenuName = NULL;
7036 cls.lpszClassName = "DialogModeParentClass";
7037 if(!RegisterClassA(&cls)) assert(0);
7039 hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7040 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7042 /* Test richedit(ES_MULTILINE) */
7044 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7046 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7047 ok(0 == r, "expected 0, got %d\n", r);
7048 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7049 ok(2 == lcount, "expected 2, got %d\n", lcount);
7051 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7052 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7054 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7055 ok(0 == r, "expected 0, got %d\n", r);
7056 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7057 ok(3 == lcount, "expected 3, got %d\n", lcount);
7059 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7060 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7061 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7062 ok(0 == r, "expected 0, got %d\n", r);
7063 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7064 ok(3 == lcount, "expected 3, got %d\n", lcount);
7066 DestroyWindow(hwRichEdit);
7068 /* Test standalone richedit(ES_MULTILINE) */
7070 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7072 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7073 ok(0 == r, "expected 0, got %d\n", r);
7074 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7075 ok(2 == lcount, "expected 2, got %d\n", lcount);
7077 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7078 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7080 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7081 ok(0 == r, "expected 0, got %d\n", r);
7082 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7083 ok(2 == lcount, "expected 2, got %d\n", lcount);
7085 DestroyWindow(hwRichEdit);
7087 /* Check a destination for messages */
7089 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7091 SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7092 SetParent( hwRichEdit, NULL);
7094 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7095 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7097 memset(&dm_messages, 0, sizeof(dm_messages));
7098 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7099 ok(0 == r, "expected 0, got %d\n", r);
7100 test_dm_messages(0, 1, 0);
7102 memset(&dm_messages, 0, sizeof(dm_messages));
7103 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7104 ok(0 == r, "expected 0, got %d\n", r);
7105 test_dm_messages(0, 0, 1);
7107 DestroyWindow(hwRichEdit);
7109 /* Check messages from richedit(ES_MULTILINE) */
7111 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7113 memset(&dm_messages, 0, sizeof(dm_messages));
7114 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7115 ok(0 == r, "expected 0, got %d\n", r);
7116 test_dm_messages(0, 0, 0);
7118 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7119 ok(2 == lcount, "expected 2, got %d\n", lcount);
7121 memset(&dm_messages, 0, sizeof(dm_messages));
7122 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7123 ok(0 == r, "expected 0, got %d\n", r);
7124 test_dm_messages(0, 0, 0);
7126 memset(&dm_messages, 0, sizeof(dm_messages));
7127 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7128 ok(0 == r, "expected 0, got %d\n", r);
7129 test_dm_messages(0, 0, 0);
7131 memset(&dm_messages, 0, sizeof(dm_messages));
7132 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7133 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7134 test_dm_messages(0, 0, 0);
7136 memset(&dm_messages, 0, sizeof(dm_messages));
7137 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7138 ok(0 == r, "expected 0, got %d\n", r);
7139 test_dm_messages(0, 1, 0);
7141 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7142 ok(2 == lcount, "expected 2, got %d\n", lcount);
7144 memset(&dm_messages, 0, sizeof(dm_messages));
7145 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7146 ok(0 == r, "expected 0, got %d\n", r);
7147 test_dm_messages(0, 0, 0);
7149 memset(&dm_messages, 0, sizeof(dm_messages));
7150 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7151 ok(0 == r, "expected 0, got %d\n", r);
7152 test_dm_messages(0, 0, 1);
7154 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7155 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7156 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7158 memset(&dm_messages, 0, sizeof(dm_messages));
7159 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7160 ok(0 == r, "expected 0, got %d\n", r);
7161 test_dm_messages(0, 1, 1);
7163 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7164 ok(2 == lcount, "expected 2, got %d\n", lcount);
7166 DestroyWindow(hwButton);
7167 DestroyWindow(hwRichEdit);
7169 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7171 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
7173 memset(&dm_messages, 0, sizeof(dm_messages));
7174 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7175 ok(0 == r, "expected 0, got %d\n", r);
7176 test_dm_messages(0, 0, 0);
7178 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7179 ok(2 == lcount, "expected 2, got %d\n", lcount);
7181 memset(&dm_messages, 0, sizeof(dm_messages));
7182 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7183 ok(0 == r, "expected 0, got %d\n", r);
7184 test_dm_messages(0, 0, 0);
7186 memset(&dm_messages, 0, sizeof(dm_messages));
7187 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7188 ok(0 == r, "expected 0, got %d\n", r);
7189 test_dm_messages(0, 0, 0);
7191 memset(&dm_messages, 0, sizeof(dm_messages));
7192 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7193 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7194 test_dm_messages(0, 0, 0);
7196 memset(&dm_messages, 0, sizeof(dm_messages));
7197 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7198 ok(0 == r, "expected 0, got %d\n", r);
7199 test_dm_messages(0, 0, 0);
7201 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7202 ok(3 == lcount, "expected 3, got %d\n", lcount);
7204 memset(&dm_messages, 0, sizeof(dm_messages));
7205 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7206 ok(0 == r, "expected 0, got %d\n", r);
7207 test_dm_messages(0, 0, 0);
7209 memset(&dm_messages, 0, sizeof(dm_messages));
7210 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7211 ok(0 == r, "expected 0, got %d\n", r);
7212 test_dm_messages(0, 0, 1);
7214 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7215 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7216 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7218 memset(&dm_messages, 0, sizeof(dm_messages));
7219 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7220 ok(0 == r, "expected 0, got %d\n", r);
7221 test_dm_messages(0, 0, 0);
7223 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7224 ok(4 == lcount, "expected 4, got %d\n", lcount);
7226 DestroyWindow(hwButton);
7227 DestroyWindow(hwRichEdit);
7229 /* Check messages from richedit(0) */
7231 hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
7233 memset(&dm_messages, 0, sizeof(dm_messages));
7234 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7235 ok(0 == r, "expected 0, got %d\n", r);
7236 test_dm_messages(0, 0, 0);
7238 memset(&dm_messages, 0, sizeof(dm_messages));
7239 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7240 ok(0 == r, "expected 0, got %d\n", r);
7241 test_dm_messages(0, 0, 0);
7243 memset(&dm_messages, 0, sizeof(dm_messages));
7244 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7245 ok(0 == r, "expected 0, got %d\n", r);
7246 test_dm_messages(0, 0, 0);
7248 memset(&dm_messages, 0, sizeof(dm_messages));
7249 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7250 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7251 test_dm_messages(0, 0, 0);
7253 memset(&dm_messages, 0, sizeof(dm_messages));
7254 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7255 ok(0 == r, "expected 0, got %d\n", r);
7256 test_dm_messages(0, 1, 0);
7258 memset(&dm_messages, 0, sizeof(dm_messages));
7259 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7260 ok(0 == r, "expected 0, got %d\n", r);
7261 test_dm_messages(0, 0, 0);
7263 memset(&dm_messages, 0, sizeof(dm_messages));
7264 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7265 ok(0 == r, "expected 0, got %d\n", r);
7266 test_dm_messages(0, 0, 1);
7268 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7269 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7270 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7272 memset(&dm_messages, 0, sizeof(dm_messages));
7273 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7274 ok(0 == r, "expected 0, got %d\n", r);
7275 test_dm_messages(0, 1, 1);
7277 DestroyWindow(hwRichEdit);
7279 /* Check messages from richedit(ES_WANTRETURN) */
7281 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
7283 memset(&dm_messages, 0, sizeof(dm_messages));
7284 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7285 ok(0 == r, "expected 0, got %d\n", r);
7286 test_dm_messages(0, 0, 0);
7288 memset(&dm_messages, 0, sizeof(dm_messages));
7289 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7290 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7291 test_dm_messages(0, 0, 0);
7293 memset(&dm_messages, 0, sizeof(dm_messages));
7294 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7295 ok(0 == r, "expected 0, got %d\n", r);
7296 test_dm_messages(0, 0, 0);
7298 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7299 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7300 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7302 memset(&dm_messages, 0, sizeof(dm_messages));
7303 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7304 ok(0 == r, "expected 0, got %d\n", r);
7305 test_dm_messages(0, 0, 0);
7307 DestroyWindow(hwRichEdit);
7308 DestroyWindow(hwParent);
7311 static void test_EM_FINDWORDBREAK_W(void)
7313 static const struct {
7314 WCHAR c;
7315 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7316 } delimiter_tests[] = {
7317 {0x0a, FALSE}, /* newline */
7318 {0x0b, FALSE}, /* vertical tab */
7319 {0x0c, FALSE}, /* form feed */
7320 {0x0d, FALSE}, /* carriage return */
7321 {0x20, TRUE}, /* space */
7322 {0x61, FALSE}, /* capital letter a */
7323 {0xa0, FALSE}, /* no-break space */
7324 {0x2000, FALSE}, /* en quad */
7325 {0x3000, FALSE}, /* Ideographic space */
7326 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7327 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7328 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7329 {0xac00, FALSE}, /* Hangul character GA*/
7330 {0xd7af, FALSE}, /* End of Hangul character chart */
7331 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7332 {0xff20, FALSE}, /* fullwidth commercial @ */
7333 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7335 int i;
7336 HWND hwndRichEdit = new_richeditW(NULL);
7337 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7338 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7340 WCHAR wbuf[2];
7341 int result;
7343 wbuf[0] = delimiter_tests[i].c;
7344 wbuf[1] = 0;
7345 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7346 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7347 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7348 todo_wine
7349 ok(result == delimiter_tests[i].isdelimiter,
7350 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7351 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7352 else
7353 ok(result == delimiter_tests[i].isdelimiter,
7354 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7355 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7357 DestroyWindow(hwndRichEdit);
7360 static void test_EM_FINDWORDBREAK_A(void)
7362 static const struct {
7363 WCHAR c;
7364 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7365 } delimiter_tests[] = {
7366 {0x0a, FALSE}, /* newline */
7367 {0x0b, FALSE}, /* vertical tab */
7368 {0x0c, FALSE}, /* form feed */
7369 {0x0d, FALSE}, /* carriage return */
7370 {0x20, TRUE}, /* space */
7371 {0x61, FALSE}, /* capital letter a */
7373 int i;
7374 HWND hwndRichEdit = new_richedit(NULL);
7376 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7377 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7379 int result;
7380 char buf[2];
7381 buf[0] = delimiter_tests[i].c;
7382 buf[1] = 0;
7383 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7384 result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7385 if (buf[0] == 0x20)
7386 todo_wine
7387 ok(result == delimiter_tests[i].isdelimiter,
7388 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7389 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7390 else
7391 ok(result == delimiter_tests[i].isdelimiter,
7392 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7393 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7395 DestroyWindow(hwndRichEdit);
7399 * This test attempts to show the effect of enter on a richedit
7400 * control v1.0 inserts CRLF whereas for higher versions it only
7401 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7402 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7403 * does for higher. The same test is cloned in riched32 and riched20.
7405 static void test_enter(void)
7407 static const struct {
7408 const char *initialtext;
7409 const int cursor;
7410 const char *expectedwmtext;
7411 const char *expectedemtext;
7412 const char *expectedemtextcrlf;
7413 } testenteritems[] = {
7414 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7415 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7416 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7417 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7418 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7421 char expectedbuf[1024];
7422 char resultbuf[1024];
7423 HWND hwndRichEdit = new_richedit(NULL);
7424 UINT i,j;
7426 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7428 char buf[1024] = {0};
7429 LRESULT result;
7430 GETTEXTEX getText;
7431 const char *expected;
7433 /* Set the text to the initial text */
7434 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
7435 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7437 /* Send Enter */
7438 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7439 simulate_typing_characters(hwndRichEdit, "\r");
7441 /* 1. Retrieve with WM_GETTEXT */
7442 buf[0] = 0x00;
7443 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
7444 expected = testenteritems[i].expectedwmtext;
7446 resultbuf[0]=0x00;
7447 for (j = 0; j < (UINT)result; j++)
7448 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7449 expectedbuf[0] = '\0';
7450 for (j = 0; j < strlen(expected); j++)
7451 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7453 result = strcmp(expected, buf);
7454 ok (result == 0,
7455 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7456 i, resultbuf, expectedbuf);
7458 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7459 getText.cb = sizeof(buf);
7460 getText.flags = GT_DEFAULT;
7461 getText.codepage = CP_ACP;
7462 getText.lpDefaultChar = NULL;
7463 getText.lpUsedDefChar = NULL;
7464 buf[0] = 0x00;
7465 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7466 expected = testenteritems[i].expectedemtext;
7468 resultbuf[0]=0x00;
7469 for (j = 0; j < (UINT)result; j++)
7470 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7471 expectedbuf[0] = '\0';
7472 for (j = 0; j < strlen(expected); j++)
7473 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7475 result = strcmp(expected, buf);
7476 ok (result == 0,
7477 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7478 i, resultbuf, expectedbuf);
7480 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7481 getText.cb = sizeof(buf);
7482 getText.flags = GT_USECRLF;
7483 getText.codepage = CP_ACP;
7484 getText.lpDefaultChar = NULL;
7485 getText.lpUsedDefChar = NULL;
7486 buf[0] = 0x00;
7487 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7488 expected = testenteritems[i].expectedemtextcrlf;
7490 resultbuf[0]=0x00;
7491 for (j = 0; j < (UINT)result; j++)
7492 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7493 expectedbuf[0] = '\0';
7494 for (j = 0; j < strlen(expected); j++)
7495 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7497 result = strcmp(expected, buf);
7498 ok (result == 0,
7499 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7500 i, resultbuf, expectedbuf);
7503 DestroyWindow(hwndRichEdit);
7506 static void test_WM_CREATE(void)
7508 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
7509 static const char title[] = "line1\nline2";
7511 HWND rich_edit;
7512 LRESULT res;
7513 char buf[64];
7514 int len;
7516 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
7517 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7518 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7520 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7521 ok(len == 5, "GetWindowText returned %d\n", len);
7522 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
7524 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7525 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7527 DestroyWindow(rich_edit);
7529 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
7530 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7531 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
7533 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7534 ok(len == 12, "GetWindowText returned %d\n", len);
7535 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
7537 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7538 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7540 DestroyWindow(rich_edit);
7543 /*******************************************************************
7544 * Test that after deleting all of the text, the first paragraph
7545 * format reverts to the default.
7547 static void test_reset_default_para_fmt( void )
7549 HWND richedit = new_richeditW( NULL );
7550 PARAFORMAT2 fmt;
7551 WORD def_align, new_align;
7553 memset( &fmt, 0, sizeof(fmt) );
7554 fmt.cbSize = sizeof(PARAFORMAT2);
7555 fmt.dwMask = -1;
7556 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7557 def_align = fmt.wAlignment;
7558 new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
7560 simulate_typing_characters( richedit, "123" );
7562 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7563 fmt.dwMask = PFM_ALIGNMENT;
7564 fmt.wAlignment = new_align;
7565 SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
7567 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7568 ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
7570 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7571 SendMessageA( richedit, WM_CUT, 0, 0 );
7573 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7574 ok( fmt.wAlignment == def_align, "got %d exppect %d\n", fmt.wAlignment, def_align );
7576 DestroyWindow( richedit );
7579 START_TEST( editor )
7581 BOOL ret;
7582 /* Must explicitly LoadLibrary(). The test has no references to functions in
7583 * RICHED20.DLL, so the linker doesn't actually link to it. */
7584 hmoduleRichEdit = LoadLibraryA("riched20.dll");
7585 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7587 test_WM_CHAR();
7588 test_EM_FINDTEXT(FALSE);
7589 test_EM_FINDTEXT(TRUE);
7590 test_EM_GETLINE();
7591 test_EM_POSFROMCHAR();
7592 test_EM_SCROLLCARET();
7593 test_EM_SCROLL();
7594 test_scrollbar_visibility();
7595 test_WM_SETTEXT();
7596 test_EM_LINELENGTH();
7597 test_EM_SETCHARFORMAT();
7598 test_EM_SETTEXTMODE();
7599 test_TM_PLAINTEXT();
7600 test_EM_SETOPTIONS();
7601 test_WM_GETTEXT();
7602 test_EM_GETTEXTRANGE();
7603 test_EM_GETSELTEXT();
7604 test_EM_SETUNDOLIMIT();
7605 test_ES_PASSWORD();
7606 test_EM_SETTEXTEX();
7607 test_EM_LIMITTEXT();
7608 test_EM_EXLIMITTEXT();
7609 test_EM_GETLIMITTEXT();
7610 test_WM_SETFONT();
7611 test_EM_GETMODIFY();
7612 test_EM_EXSETSEL();
7613 test_WM_PASTE();
7614 test_EM_STREAMIN();
7615 test_EM_STREAMOUT();
7616 test_EM_STREAMOUT_FONTTBL();
7617 test_EM_StreamIn_Undo();
7618 test_EM_FORMATRANGE();
7619 test_unicode_conversions();
7620 test_EM_GETTEXTLENGTHEX();
7621 test_EM_REPLACESEL(1);
7622 test_EM_REPLACESEL(0);
7623 test_WM_NOTIFY();
7624 test_EN_LINK();
7625 test_EM_AUTOURLDETECT();
7626 test_eventMask();
7627 test_undo_coalescing();
7628 test_word_movement();
7629 test_EM_CHARFROMPOS();
7630 test_SETPARAFORMAT();
7631 test_word_wrap();
7632 test_autoscroll();
7633 test_format_rect();
7634 test_WM_GETDLGCODE();
7635 test_zoom();
7636 test_dialogmode();
7637 test_EM_FINDWORDBREAK_W();
7638 test_EM_FINDWORDBREAK_A();
7639 test_enter();
7640 test_WM_CREATE();
7641 test_reset_default_para_fmt();
7643 /* Set the environment variable WINETEST_RICHED20 to keep windows
7644 * responsive and open for 30 seconds. This is useful for debugging.
7646 if (getenv( "WINETEST_RICHED20" )) {
7647 keep_responsive(30);
7650 OleFlushClipboard();
7651 ret = FreeLibrary(hmoduleRichEdit);
7652 ok(ret, "error: %d\n", (int) GetLastError());