riched20/tests: Fix a typo in a comment.
[wine.git] / dlls / riched20 / tests / editor.c
blob4f4d637b687d3361c22adeafd0395e3099c317fb
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 #define COBJMACROS
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <assert.h>
28 #include <windef.h>
29 #include <winbase.h>
30 #include <wingdi.h>
31 #include <winuser.h>
32 #include <winnls.h>
33 #include <ole2.h>
34 #include <richedit.h>
35 #include <richole.h>
36 #include <imm.h>
37 #include <textserv.h>
38 #include <commdlg.h>
39 #include <time.h>
40 #include <wine/test.h>
42 #define ID_RICHEDITTESTDBUTTON 0x123
44 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
46 #define ok_w3(format, szString1, szString2, szString3) \
47 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
48 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
49 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
50 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
51 format, string1, string2, string3);
53 static HMODULE hmoduleRichEdit;
54 static BOOL is_lang_japanese;
56 #if defined(__i386__) && !defined(__MINGW32__) && (!defined(_MSC_VER) || !defined(__clang__))
57 static void disable_beep( HWND hwnd )
59 /* don't attempt to disable beep if we don't have thiscall compiler support */
61 #else
62 #define ITextServices_OnTxPropertyBitsChange(This,a,b) (This)->lpVtbl->OnTxPropertyBitsChange(This,a,b)
63 static void disable_beep( HWND hwnd )
65 IRichEditOle *richole;
66 ITextServices *services;
67 IID *pIID_ITextServices = (IID *)GetProcAddress( hmoduleRichEdit, "IID_ITextServices" );
69 if (SendMessageW( hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&richole ))
71 if (SUCCEEDED( IRichEditOle_QueryInterface( richole, pIID_ITextServices, (void **)&services ) ))
73 ITextServices_OnTxPropertyBitsChange( services, TXTBIT_ALLOWBEEP, 0 );
74 ITextServices_Release( services );
76 IRichEditOle_Release( richole );
79 #endif
81 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
82 HWND hwnd;
83 hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
84 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
85 hmoduleRichEdit, NULL);
86 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
87 disable_beep( hwnd );
88 return hwnd;
91 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
92 HWND hwnd;
93 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
94 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
95 hmoduleRichEdit, NULL);
96 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
97 disable_beep( hwnd );
98 return hwnd;
101 static HWND new_richedit(HWND parent) {
102 return new_window(RICHEDIT_CLASS20A, ES_MULTILINE, parent);
105 static HWND new_richedit_with_style(HWND parent, DWORD style) {
106 return new_window(RICHEDIT_CLASS20A, style, parent);
109 static HWND new_richeditW(HWND parent) {
110 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
113 static WNDCLASSA make_simple_class(WNDPROC wndproc, LPCSTR lpClassName)
115 WNDCLASSA cls;
116 cls.style = 0;
117 cls.lpfnWndProc = wndproc;
118 cls.cbClsExtra = 0;
119 cls.cbWndExtra = 0;
120 cls.hInstance = GetModuleHandleA(0);
121 cls.hIcon = 0;
122 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
123 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
124 cls.lpszMenuName = NULL;
125 cls.lpszClassName = lpClassName;
126 return cls;
129 /* Keeps the window reponsive for the deley_time in seconds.
130 * This is useful for debugging a test to see what is happening. */
131 static void keep_responsive(time_t delay_time)
133 MSG msg;
134 time_t end;
136 /* The message pump uses PeekMessage() to empty the queue and then
137 * sleeps for 50ms before retrying the queue. */
138 end = time(NULL) + delay_time;
139 while (time(NULL) < end) {
140 if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
141 TranslateMessage(&msg);
142 DispatchMessageA(&msg);
143 } else {
144 Sleep(50);
149 static void simulate_typing_characters(HWND hwnd, const char* szChars)
151 int ret;
153 while (*szChars != '\0') {
154 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
155 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
156 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
157 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
158 szChars++;
162 static BOOL hold_key(int vk)
164 BYTE key_state[256];
165 BOOL result;
167 result = GetKeyboardState(key_state);
168 ok(result, "GetKeyboardState failed.\n");
169 if (!result) return FALSE;
170 key_state[vk] |= 0x80;
171 result = SetKeyboardState(key_state);
172 ok(result, "SetKeyboardState failed.\n");
173 return result != 0;
176 static BOOL release_key(int vk)
178 BYTE key_state[256];
179 BOOL result;
181 result = GetKeyboardState(key_state);
182 ok(result, "GetKeyboardState failed.\n");
183 if (!result) return FALSE;
184 key_state[vk] &= ~0x80;
185 result = SetKeyboardState(key_state);
186 ok(result, "SetKeyboardState failed.\n");
187 return result != 0;
190 static const char haystack[] = "WINEWine wineWine wine WineWine";
191 /* ^0 ^10 ^20 ^30 */
193 struct find_s {
194 int start;
195 int end;
196 const char *needle;
197 int flags;
198 int expected_loc;
202 static struct find_s find_tests[] = {
203 /* Find in empty text */
204 {0, -1, "foo", FR_DOWN, -1},
205 {0, -1, "foo", 0, -1},
206 {0, -1, "", FR_DOWN, -1},
207 {20, 5, "foo", FR_DOWN, -1},
208 {5, 20, "foo", FR_DOWN, -1}
211 static struct find_s find_tests2[] = {
212 /* No-result find */
213 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
214 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
216 /* Subsequent finds */
217 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
218 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
219 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
220 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
222 /* Find backwards */
223 {19, 20, "Wine", FR_MATCHCASE, 13},
224 {10, 20, "Wine", FR_MATCHCASE, 4},
225 {20, 10, "Wine", FR_MATCHCASE, 13},
227 /* Case-insensitive */
228 {1, 31, "wInE", FR_DOWN, 4},
229 {1, 31, "Wine", FR_DOWN, 4},
231 /* High-to-low ranges */
232 {20, 5, "Wine", FR_DOWN, -1},
233 {2, 1, "Wine", FR_DOWN, -1},
234 {30, 29, "Wine", FR_DOWN, -1},
235 {20, 5, "Wine", 0, 13},
237 /* Find nothing */
238 {5, 10, "", FR_DOWN, -1},
239 {10, 5, "", FR_DOWN, -1},
240 {0, -1, "", FR_DOWN, -1},
241 {10, 5, "", 0, -1},
243 /* Whole-word search */
244 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
245 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
246 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
247 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
248 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
249 {11, -1, "winewine", FR_WHOLEWORD, 0},
250 {31, -1, "winewine", FR_WHOLEWORD, 23},
252 /* Bad ranges */
253 {5, 200, "XXX", FR_DOWN, -1},
254 {-20, 20, "Wine", FR_DOWN, -1},
255 {-20, 20, "Wine", FR_DOWN, -1},
256 {-15, -20, "Wine", FR_DOWN, -1},
257 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
259 /* Check the case noted in bug 4479 where matches at end aren't recognized */
260 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
261 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
262 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
263 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
264 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
266 /* The backwards case of bug 4479; bounds look right
267 * Fails because backward find is wrong */
268 {19, 20, "WINE", FR_MATCHCASE, 0},
269 {0, 20, "WINE", FR_MATCHCASE, -1},
271 {0, -1, "wineWine wine", 0, -1},
274 static WCHAR *atowstr(const char *str)
276 WCHAR *ret;
277 DWORD len;
278 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
279 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
280 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
281 return ret;
284 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
286 int findloc;
288 if(unicode){
289 FINDTEXTW ftw;
290 memset(&ftw, 0, sizeof(ftw));
291 ftw.chrg.cpMin = f->start;
292 ftw.chrg.cpMax = f->end;
293 ftw.lpstrText = atowstr(f->needle);
295 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
296 ok(findloc == f->expected_loc,
297 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
298 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
300 findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
301 ok(findloc == f->expected_loc,
302 "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
303 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
305 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
306 }else{
307 FINDTEXTA fta;
308 memset(&fta, 0, sizeof(fta));
309 fta.chrg.cpMin = f->start;
310 fta.chrg.cpMax = f->end;
311 fta.lpstrText = f->needle;
313 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
314 ok(findloc == f->expected_loc,
315 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
316 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
320 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
321 int id, BOOL unicode)
323 int findloc;
324 int expected_end_loc;
326 if(unicode){
327 FINDTEXTEXW ftw;
328 memset(&ftw, 0, sizeof(ftw));
329 ftw.chrg.cpMin = f->start;
330 ftw.chrg.cpMax = f->end;
331 ftw.lpstrText = atowstr(f->needle);
332 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
333 ok(findloc == f->expected_loc,
334 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
335 name, id, f->needle, f->start, f->end, f->flags, findloc);
336 ok(ftw.chrgText.cpMin == f->expected_loc,
337 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %ld\n",
338 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
339 expected_end_loc = ((f->expected_loc == -1) ? -1
340 : f->expected_loc + strlen(f->needle));
341 ok(ftw.chrgText.cpMax == expected_end_loc,
342 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %ld, expected %d\n",
343 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
344 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
345 }else{
346 FINDTEXTEXA fta;
347 memset(&fta, 0, sizeof(fta));
348 fta.chrg.cpMin = f->start;
349 fta.chrg.cpMax = f->end;
350 fta.lpstrText = f->needle;
351 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
352 ok(findloc == f->expected_loc,
353 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
354 name, id, f->needle, f->start, f->end, f->flags, findloc);
355 ok(fta.chrgText.cpMin == f->expected_loc,
356 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %ld\n",
357 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
358 expected_end_loc = ((f->expected_loc == -1) ? -1
359 : f->expected_loc + strlen(f->needle));
360 ok(fta.chrgText.cpMax == expected_end_loc,
361 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %ld, expected %d\n",
362 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
366 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
367 int num_tests, BOOL unicode)
369 int i;
371 for (i = 0; i < num_tests; i++) {
372 check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
373 check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
377 static void test_EM_FINDTEXT(BOOL unicode)
379 HWND hwndRichEdit;
380 CHARFORMAT2A cf2;
382 if(unicode)
383 hwndRichEdit = new_richeditW(NULL);
384 else
385 hwndRichEdit = new_richedit(NULL);
387 /* Empty rich edit control */
388 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests, ARRAY_SIZE(find_tests), unicode);
390 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
392 /* Haystack text */
393 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2, ARRAY_SIZE(find_tests2), unicode);
395 /* Setting a format on an arbitrary range should have no effect in search
396 results. This tests correct offset reporting across runs. */
397 cf2.cbSize = sizeof(CHARFORMAT2A);
398 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
399 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
400 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
401 SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
402 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
404 /* Haystack text, again */
405 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2, ARRAY_SIZE(find_tests2), unicode);
407 /* Yet another range */
408 cf2.dwMask = CFM_BOLD | cf2.dwMask;
409 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
410 SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
411 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
413 /* Haystack text, again */
414 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2, ARRAY_SIZE(find_tests2), unicode);
416 DestroyWindow(hwndRichEdit);
419 static const struct getline_s {
420 int line;
421 size_t buffer_len;
422 const char *text;
423 } gl[] = {
424 {0, 10, "foo bar\r"},
425 {1, 10, "\r"},
426 {2, 10, "bar\r"},
427 {3, 10, "\r"},
429 /* Buffer smaller than line length */
430 {0, 2, "foo bar\r"},
431 {0, 1, "foo bar\r"},
432 {0, 0, "foo bar\r"}
435 static void test_EM_GETLINE(void)
437 int i;
438 HWND hwndRichEdit = new_richedit(NULL);
439 static const int nBuf = 1024;
440 char dest[1024], origdest[1024];
441 const char text[] = "foo bar\n"
442 "\n"
443 "bar\n";
445 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
447 memset(origdest, 0xBB, nBuf);
448 for (i = 0; i < ARRAY_SIZE(gl); i++)
450 int nCopied;
451 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
452 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
453 memset(dest, 0xBB, nBuf);
454 *(WORD *) dest = gl[i].buffer_len;
456 /* EM_GETLINE appends a "\r\0" to the end of the line
457 * nCopied counts up to and including the '\r' */
458 nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
459 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
460 expected_nCopied);
461 /* two special cases since a parameter is passed via dest */
462 if (gl[i].buffer_len == 0)
463 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
464 "buffer_len=0\n");
465 else if (gl[i].buffer_len == 1)
466 ok(dest[0] == gl[i].text[0] && !dest[1] &&
467 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
468 else
470 /* Prepare hex strings of buffers to dump on failure. */
471 char expectedbuf[1024];
472 char resultbuf[1024];
473 int j;
474 resultbuf[0] = '\0';
475 for (j = 0; j < 32; j++)
476 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
477 expectedbuf[0] = '\0';
478 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
479 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
480 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
481 sprintf(expectedbuf+strlen(expectedbuf), "??");
482 for (; j < 32; j++) /* Bytes after declared buffer size */
483 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
485 /* Test the part of the buffer that is expected to be written according
486 * to the MSDN documentation fo EM_GETLINE, which does not state that
487 * a NULL terminating character will be added unless no text is copied.
489 * Windows NT does not append a NULL terminating character, but
490 * Windows 2000 and up do append a NULL terminating character if there
491 * is space in the buffer. The test will ignore this difference. */
492 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
493 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
494 i, expected_bytes_written, expectedbuf, resultbuf);
495 /* Test the part of the buffer after the declared length to make sure
496 * there are no buffer overruns. */
497 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
498 nBuf - gl[i].buffer_len),
499 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
500 i, expected_bytes_written, expectedbuf, resultbuf);
504 DestroyWindow(hwndRichEdit);
507 static void test_EM_LINELENGTH(void)
509 HWND hwndRichEdit = new_richedit(NULL);
510 const char * text =
511 "richedit1\r"
512 "richedit1\n"
513 "richedit1\r\n"
514 "richedit1";
515 int offset_test[10][2] = {
516 {0, 9},
517 {5, 9},
518 {10, 9},
519 {15, 9},
520 {20, 9},
521 {25, 9},
522 {30, 9},
523 {35, 9},
524 {40, 0},
525 {45, 0},
527 int i;
528 LRESULT result;
530 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
532 for (i = 0; i < 10; i++) {
533 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
534 ok(result == offset_test[i][1], "Length of line at offset %d is %Id, expected %d\n",
535 offset_test[i][0], result, offset_test[i][1]);
538 /* Test with multibyte character */
539 if (!is_lang_japanese)
540 skip("Skip multibyte character tests on non-Japanese platform\n");
541 else
543 const char *text1 =
544 "wine\n"
545 "richedit\x8e\xf0\n"
546 "wine";
547 int offset_test1[3][2] = {
548 {0, 4}, /* Line 1: |wine\n */
549 {5, 9}, /* Line 2: |richedit\x8e\xf0\n */
550 {15, 4}, /* Line 3: |wine */
552 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
553 for (i = 0; i < ARRAY_SIZE(offset_test1); i++) {
554 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
555 ok(result == offset_test1[i][1], "Length of line at offset %d is %Id, expected %d\n",
556 offset_test1[i][0], result, offset_test1[i][1]);
560 DestroyWindow(hwndRichEdit);
563 static int get_scroll_pos_y(HWND hwnd)
565 POINT p = {-1, -1};
566 SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&p);
567 ok(p.x != -1 && p.y != -1, "p.x:%ld p.y:%ld\n", p.x, p.y);
568 return p.y;
571 static void move_cursor(HWND hwnd, LONG charindex)
573 CHARRANGE cr;
574 cr.cpMax = charindex;
575 cr.cpMin = charindex;
576 SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
579 static void line_scroll(HWND hwnd, int amount)
581 SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
584 static void test_EM_SCROLLCARET(void)
586 int prevY, curY;
587 const char text[] = "aa\n"
588 "this is a long line of text that should be longer than the "
589 "control's width\n"
590 "cc\n"
591 "dd\n"
592 "ee\n"
593 "ff\n"
594 "gg\n"
595 "hh\n";
596 /* The richedit window height needs to be large enough vertically to fit in
597 * more than two lines of text, so the new_richedit function can't be used
598 * since a height of 60 was not large enough on some systems.
600 HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
601 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
602 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
603 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
605 /* Can't verify this */
606 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
608 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
610 /* Caret above visible window */
611 line_scroll(hwndRichEdit, 3);
612 prevY = get_scroll_pos_y(hwndRichEdit);
613 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
614 curY = get_scroll_pos_y(hwndRichEdit);
615 ok(prevY != curY, "%d == %d\n", prevY, curY);
617 /* Caret below visible window */
618 move_cursor(hwndRichEdit, sizeof(text) - 1);
619 line_scroll(hwndRichEdit, -3);
620 prevY = get_scroll_pos_y(hwndRichEdit);
621 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
622 curY = get_scroll_pos_y(hwndRichEdit);
623 ok(prevY != curY, "%d == %d\n", prevY, curY);
625 /* Caret in visible window */
626 move_cursor(hwndRichEdit, sizeof(text) - 2);
627 prevY = get_scroll_pos_y(hwndRichEdit);
628 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
629 curY = get_scroll_pos_y(hwndRichEdit);
630 ok(prevY == curY, "%d != %d\n", prevY, curY);
632 /* Caret still in visible window */
633 line_scroll(hwndRichEdit, -1);
634 prevY = get_scroll_pos_y(hwndRichEdit);
635 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
636 curY = get_scroll_pos_y(hwndRichEdit);
637 ok(prevY == curY, "%d != %d\n", prevY, curY);
639 DestroyWindow(hwndRichEdit);
642 static void test_EM_POSFROMCHAR(void)
644 HWND hwndRichEdit = new_richedit(NULL);
645 int i, expected;
646 LRESULT result;
647 unsigned int height = 0;
648 int xpos = 0;
649 POINTL pt;
650 LOCALESIGNATURE sig;
651 BOOL rtl;
652 PARAFORMAT2 fmt;
653 static const char text[] = "aa\n"
654 "this is a long line of text that should be longer than the "
655 "control's width\n"
656 "cc\n"
657 "dd\n"
658 "ee\n"
659 "ff\n"
660 "gg\n"
661 "hh\n";
663 rtl = (GetLocaleInfoA(LOCALE_SYSTEM_DEFAULT, LOCALE_FONTSIGNATURE,
664 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
665 (sig.lsUsb[3] & 0x08000000) != 0);
667 /* Fill the control to lines to ensure that most of them are offscreen */
668 for (i = 0; i < 50; i++)
670 /* Do not modify the string; it is exactly 16 characters long. */
671 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
672 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
676 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
677 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
678 Richedit 3.0 accepts either of the above API conventions.
681 /* Testing Richedit 2.0 API format */
683 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
684 Since all lines are identical and drawn with the same font,
685 they should have the same height... right?
687 for (i = 0; i < 50; i++)
689 /* All the lines are 16 characters long */
690 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
691 if (i == 0)
693 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
694 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
695 xpos = LOWORD(result);
697 else if (i == 1)
699 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
700 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
701 height = HIWORD(result);
703 else
705 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
706 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
710 /* Testing position at end of text */
711 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
712 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
713 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
715 /* Testing position way past end of text */
716 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
717 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
718 expected = (rtl ? 8 : 1);
719 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
721 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
722 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
723 for (i = 0; i < 50; i++)
725 /* All the lines are 16 characters long */
726 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
727 ok((signed short)(HIWORD(result)) == (i - 1) * height,
728 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
729 (signed short)(HIWORD(result)), (i - 1) * height);
730 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
733 /* Testing position at end of text */
734 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
735 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
736 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
738 /* Testing position way past end of text */
739 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
740 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
741 expected = (rtl ? 8 : 1);
742 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
744 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
745 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
746 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
748 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
749 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
750 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
751 xpos = LOWORD(result);
753 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
754 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
755 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
756 ok((signed short)(LOWORD(result)) < xpos,
757 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
758 (signed short)(LOWORD(result)), xpos);
759 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
761 /* Test around end of text that doesn't end in a newline. */
762 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
763 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
764 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
765 ok(pt.x > 1, "pt.x = %ld\n", pt.x);
766 xpos = pt.x;
767 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
768 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
769 ok(pt.x > xpos, "pt.x = %ld\n", pt.x);
770 xpos = (rtl ? pt.x + 7 : pt.x);
771 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
772 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
773 ok(pt.x == xpos, "pt.x = %ld\n", pt.x);
775 /* Try a negative position. */
776 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
777 ok(pt.x == 1, "pt.x = %ld\n", pt.x);
779 /* test negative indentation */
780 SendMessageA(hwndRichEdit, WM_SETTEXT, 0,
781 (LPARAM)"{\\rtf1\\pard\\fi-200\\li-200\\f1 TestSomeText\\par}");
782 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, 0);
783 ok(pt.x == 1, "pt.x = %ld\n", pt.x);
785 fmt.cbSize = sizeof(fmt);
786 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
787 ok(fmt.dxStartIndent == -400, "got %ld\n", fmt.dxStartIndent);
788 ok(fmt.dxOffset == 200, "got %ld\n", fmt.dxOffset);
789 ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
791 DestroyWindow(hwndRichEdit);
794 static void test_EM_SETCHARFORMAT(void)
796 HWND hwndRichEdit = new_richedit(NULL);
797 CHARFORMAT2A cf2;
798 CHARFORMAT2W cfW;
799 CHARFORMATA cf1a;
800 CHARFORMATW cf1w;
801 int rc = 0;
802 int tested_effects[] = {
803 CFE_BOLD,
804 CFE_ITALIC,
805 CFE_UNDERLINE,
806 CFE_STRIKEOUT,
807 CFE_PROTECTED,
808 CFE_LINK,
809 CFE_SUBSCRIPT,
810 CFE_SUPERSCRIPT,
813 int i;
814 CHARRANGE cr;
815 LOCALESIGNATURE sig;
816 BOOL rtl;
817 DWORD expect_effects;
819 rtl = (GetLocaleInfoA(LOCALE_SYSTEM_DEFAULT, LOCALE_FONTSIGNATURE,
820 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
821 (sig.lsUsb[3] & 0x08000000) != 0);
823 /* check charformat defaults */
824 memset(&cf2, 0, sizeof(CHARFORMAT2A));
825 cf2.cbSize = sizeof(CHARFORMAT2A);
826 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
827 ok(cf2.dwMask == CFM_ALL2, "got %08lx\n", cf2.dwMask);
828 expect_effects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
829 if (cf2.wWeight > 550) expect_effects |= CFE_BOLD;
830 ok(cf2.dwEffects == expect_effects, "got %08lx\n", cf2.dwEffects);
831 ok(cf2.yOffset == 0, "got %ld\n", cf2.yOffset);
832 ok(cf2.sSpacing == 0, "got %d\n", cf2.sSpacing);
833 ok(cf2.lcid == GetSystemDefaultLCID(), "got %lx\n", cf2.lcid);
834 ok(cf2.sStyle == 0, "got %d\n", cf2.sStyle);
835 ok(cf2.wKerning == 0, "got %d\n", cf2.wKerning);
836 ok(cf2.bAnimation == 0, "got %d\n", cf2.bAnimation);
837 ok(cf2.bRevAuthor == 0, "got %d\n", cf2.bRevAuthor);
839 /* Invalid flags, CHARFORMAT2 structure blanked out */
840 memset(&cf2, 0, sizeof(cf2));
841 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
842 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
844 /* A valid flag, CHARFORMAT2 structure blanked out */
845 memset(&cf2, 0, sizeof(cf2));
846 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
847 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
849 /* A valid flag, CHARFORMAT2 structure blanked out */
850 memset(&cf2, 0, sizeof(cf2));
851 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
852 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
854 /* A valid flag, CHARFORMAT2 structure blanked out */
855 memset(&cf2, 0, sizeof(cf2));
856 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
857 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
859 /* A valid flag, CHARFORMAT2 structure blanked out */
860 memset(&cf2, 0, sizeof(cf2));
861 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
862 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
864 /* Invalid flags, CHARFORMAT2 structure minimally filled */
865 memset(&cf2, 0, sizeof(cf2));
866 cf2.cbSize = sizeof(CHARFORMAT2A);
867 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
868 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
869 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
870 ok(rc == FALSE, "Should not be able to undo here.\n");
871 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
873 /* A valid flag, CHARFORMAT2 structure minimally filled */
874 memset(&cf2, 0, sizeof(cf2));
875 cf2.cbSize = sizeof(CHARFORMAT2A);
876 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
877 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
878 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
879 ok(rc == FALSE, "Should not be able to undo here.\n");
880 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
882 /* A valid flag, CHARFORMAT2 structure minimally filled */
883 memset(&cf2, 0, sizeof(cf2));
884 cf2.cbSize = sizeof(CHARFORMAT2A);
885 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
886 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
887 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
888 ok(rc == FALSE, "Should not be able to undo here.\n");
889 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
891 /* A valid flag, CHARFORMAT2 structure minimally filled */
892 memset(&cf2, 0, sizeof(cf2));
893 cf2.cbSize = sizeof(CHARFORMAT2A);
894 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
895 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
896 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
897 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
898 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
900 /* A valid flag, CHARFORMAT2 structure minimally filled */
901 memset(&cf2, 0, sizeof(cf2));
902 cf2.cbSize = sizeof(CHARFORMAT2A);
903 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
904 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
905 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
906 ok(rc == TRUE, "Should not be able to undo here.\n");
907 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
909 cf2.cbSize = sizeof(CHARFORMAT2A);
910 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
912 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
913 cf2.cbSize = sizeof(CHARFORMAT2A);
914 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
915 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
916 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
918 /* wParam==0 is default char format, does not set modify */
919 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
920 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
921 ok(rc == 0, "Text marked as modified, expected not modified!\n");
922 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
923 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
924 if (! rtl)
926 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
927 ok(rc == 0, "Text marked as modified, expected not modified!\n");
929 else
930 skip("RTL language found\n");
932 /* wParam==SCF_SELECTION sets modify if nonempty selection */
933 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
934 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
935 ok(rc == 0, "Text marked as modified, expected not modified!\n");
936 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
937 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
938 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
939 ok(rc == 0, "Text marked as modified, expected not modified!\n");
941 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
942 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
943 ok(rc == 0, "Text marked as modified, expected not modified!\n");
944 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
945 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
946 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
947 ok(rc == 0, "Text marked as modified, expected not modified!\n");
948 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
949 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
950 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
951 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
952 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
954 /* wParam==SCF_ALL sets modify regardless of whether text is present */
955 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
956 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
957 ok(rc == 0, "Text marked as modified, expected not modified!\n");
958 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
959 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
960 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
961 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
963 DestroyWindow(hwndRichEdit);
965 /* EM_GETCHARFORMAT tests */
966 for (i = 0; tested_effects[i]; i++)
968 hwndRichEdit = new_richedit(NULL);
969 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
971 /* Need to set a TrueType font to get consistent CFM_BOLD results */
972 memset(&cf2, 0, sizeof(CHARFORMAT2A));
973 cf2.cbSize = sizeof(CHARFORMAT2A);
974 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
975 cf2.dwEffects = 0;
976 strcpy(cf2.szFaceName, "Courier New");
977 cf2.wWeight = FW_DONTCARE;
978 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
980 memset(&cf2, 0, sizeof(CHARFORMAT2A));
981 cf2.cbSize = sizeof(CHARFORMAT2A);
982 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
983 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
984 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
985 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
987 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
988 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
989 ok((cf2.dwEffects & tested_effects[i]) == 0,
990 "%d, cf2.dwEffects == 0x%08lx expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
992 memset(&cf2, 0, sizeof(CHARFORMAT2A));
993 cf2.cbSize = sizeof(CHARFORMAT2A);
994 cf2.dwMask = tested_effects[i];
995 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
996 cf2.dwMask = CFM_SUPERSCRIPT;
997 cf2.dwEffects = tested_effects[i];
998 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
999 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1001 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1002 cf2.cbSize = sizeof(CHARFORMAT2A);
1003 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
1004 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1005 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1006 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1008 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1009 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1010 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1011 "%d, cf2.dwEffects == 0x%08lx expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1013 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1014 cf2.cbSize = sizeof(CHARFORMAT2A);
1015 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1016 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1017 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1018 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1020 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1021 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1022 ok((cf2.dwEffects & tested_effects[i]) == 0,
1023 "%d, cf2.dwEffects == 0x%08lx expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
1025 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1026 cf2.cbSize = sizeof(CHARFORMAT2A);
1027 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1028 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1029 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1030 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1032 (cf2.dwMask & tested_effects[i]) == 0),
1033 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1035 DestroyWindow(hwndRichEdit);
1038 for (i = 0; tested_effects[i]; i++)
1040 hwndRichEdit = new_richedit(NULL);
1041 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1043 /* Need to set a TrueType font to get consistent CFM_BOLD results */
1044 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1045 cf2.cbSize = sizeof(CHARFORMAT2A);
1046 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
1047 cf2.dwEffects = 0;
1048 strcpy(cf2.szFaceName, "Courier New");
1049 cf2.wWeight = FW_DONTCARE;
1050 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1052 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1053 cf2.cbSize = sizeof(CHARFORMAT2A);
1054 cf2.dwMask = tested_effects[i];
1055 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
1056 cf2.dwMask = CFM_SUPERSCRIPT;
1057 cf2.dwEffects = tested_effects[i];
1058 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1059 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1061 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1062 cf2.cbSize = sizeof(CHARFORMAT2A);
1063 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
1064 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1065 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1066 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1068 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1069 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1070 ok((cf2.dwEffects & tested_effects[i]) == 0,
1071 "%d, cf2.dwEffects == 0x%08lx expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
1073 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1074 cf2.cbSize = sizeof(CHARFORMAT2A);
1075 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1076 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1077 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1078 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1080 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1081 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1082 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1083 "%d, cf2.dwEffects == 0x%08lx expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1085 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1086 cf2.cbSize = sizeof(CHARFORMAT2A);
1087 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1088 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1089 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1090 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1092 (cf2.dwMask & tested_effects[i]) == 0),
1093 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1094 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1095 "%d, cf2.dwEffects == 0x%08lx expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1097 DestroyWindow(hwndRichEdit);
1100 /* Effects applied on an empty selection should take effect when selection is
1101 replaced with text */
1102 hwndRichEdit = new_richedit(NULL);
1103 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1104 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1106 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1107 cf2.cbSize = sizeof(CHARFORMAT2A);
1108 cf2.dwMask = CFM_BOLD;
1109 cf2.dwEffects = CFE_BOLD;
1110 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1112 /* Selection is now nonempty */
1113 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1115 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1116 cf2.cbSize = sizeof(CHARFORMAT2A);
1117 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1118 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1120 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1121 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1122 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1123 "%d, cf2.dwEffects == 0x%08lx expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1126 /* Set two effects on an empty selection */
1127 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1128 /* first clear bold, italic */
1129 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1130 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1131 cf2.cbSize = sizeof(CHARFORMAT2A);
1132 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1133 cf2.dwEffects = 0;
1134 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1136 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1138 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1139 cf2.cbSize = sizeof(CHARFORMAT2A);
1140 cf2.dwMask = CFM_BOLD;
1141 cf2.dwEffects = CFE_BOLD;
1142 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1143 cf2.dwMask = CFM_ITALIC;
1144 cf2.dwEffects = CFE_ITALIC;
1145 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1147 /* Selection is now nonempty */
1148 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1150 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1151 cf2.cbSize = sizeof(CHARFORMAT2A);
1152 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1153 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1155 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1156 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1157 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1158 "%d, cf2.dwEffects == 0x%08lx expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1160 /* Setting the (empty) selection to exactly the same place as before should
1161 NOT clear the insertion style! */
1162 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1163 /* first clear bold, italic */
1164 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1165 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1166 cf2.cbSize = sizeof(CHARFORMAT2A);
1167 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1168 cf2.dwEffects = 0;
1169 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1171 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1173 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1174 cf2.cbSize = sizeof(CHARFORMAT2A);
1175 cf2.dwMask = CFM_BOLD;
1176 cf2.dwEffects = CFE_BOLD;
1177 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1179 /* Empty selection in same place, insert style should NOT be forgotten here. */
1180 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1182 /* Selection is now nonempty */
1183 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1185 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1186 cf2.cbSize = sizeof(CHARFORMAT2A);
1187 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1188 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1190 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1191 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1192 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1193 "%d, cf2.dwEffects == 0x%08lx expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1195 /* Moving the selection will clear the insertion style */
1196 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1197 /* first clear bold, italic */
1198 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1199 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1200 cf2.cbSize = sizeof(CHARFORMAT2A);
1201 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1202 cf2.dwEffects = 0;
1203 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1205 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1207 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1208 cf2.cbSize = sizeof(CHARFORMAT2A);
1209 cf2.dwMask = CFM_BOLD;
1210 cf2.dwEffects = CFE_BOLD;
1211 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1213 /* Move selection and then put it back, insert style should be forgotten here. */
1214 SendMessageA(hwndRichEdit, EM_SETSEL, 3, 3);
1215 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1217 /* Selection is now nonempty */
1218 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1220 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1221 cf2.cbSize = sizeof(CHARFORMAT2A);
1222 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1223 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1225 ok(((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1226 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1227 ok((cf2.dwEffects & CFE_BOLD) == 0,
1228 "%d, cf2.dwEffects == 0x%08lx not expecting effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1230 /* Ditto with EM_EXSETSEL */
1231 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1232 /* first clear bold, italic */
1233 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1234 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1235 cf2.cbSize = sizeof(CHARFORMAT2A);
1236 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1237 cf2.dwEffects = 0;
1238 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1240 cr.cpMin = 2; cr.cpMax = 2;
1241 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1243 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1244 cf2.cbSize = sizeof(CHARFORMAT2A);
1245 cf2.dwMask = CFM_BOLD;
1246 cf2.dwEffects = CFE_BOLD;
1247 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1249 /* Empty selection in same place, insert style should NOT be forgotten here. */
1250 cr.cpMin = 2; cr.cpMax = 2;
1251 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1253 /* Selection is now nonempty */
1254 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1256 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1257 cf2.cbSize = sizeof(CHARFORMAT2A);
1258 cr.cpMin = 2; cr.cpMax = 6;
1259 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1260 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1262 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1263 "%d, cf2.dwMask == 0x%08lx expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1264 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1265 "%d, cf2.dwEffects == 0x%08lx expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1267 /* show that wWeight is at the correct offset in CHARFORMAT2A */
1268 memset(&cf2, 0, sizeof(cf2));
1269 cf2.cbSize = sizeof(cf2);
1270 cf2.dwMask = CFM_WEIGHT;
1271 cf2.wWeight = 100;
1272 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1273 memset(&cf2, 0, sizeof(cf2));
1274 cf2.cbSize = sizeof(cf2);
1275 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1276 ok(cf2.wWeight == 100, "got %d\n", cf2.wWeight);
1278 memset(&cf2, 0, sizeof(cf2));
1279 cf2.cbSize = sizeof(cf2);
1280 cf2.dwMask = CFM_SPACING;
1281 cf2.sSpacing = 10;
1282 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1283 memset(&cf2, 0, sizeof(cf2));
1284 cf2.cbSize = sizeof(cf2);
1285 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1286 ok(cf2.sSpacing == 10, "got %d\n", cf2.sSpacing);
1288 /* show that wWeight is at the correct offset in CHARFORMAT2W */
1289 memset(&cfW, 0, sizeof(cfW));
1290 cfW.cbSize = sizeof(cfW);
1291 cfW.dwMask = CFM_WEIGHT;
1292 cfW.wWeight = 100;
1293 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1294 memset(&cfW, 0, sizeof(cfW));
1295 cfW.cbSize = sizeof(cfW);
1296 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1297 ok(cfW.wWeight == 100, "got %d\n", cfW.wWeight);
1299 memset(&cfW, 0, sizeof(cfW));
1300 cfW.cbSize = sizeof(cfW);
1301 cfW.dwMask = CFM_SPACING;
1302 cfW.sSpacing = 10;
1303 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1304 memset(&cfW, 0, sizeof(cfW));
1305 cfW.cbSize = sizeof(cfW);
1306 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfW);
1307 ok(cfW.sSpacing == 10, "got %d\n", cfW.sSpacing);
1309 /* test CFE_UNDERLINE and bUnderlineType interaction */
1310 /* clear bold, italic */
1311 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1312 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1313 cf2.cbSize = sizeof(CHARFORMAT2A);
1314 cf2.dwMask = CFM_BOLD | CFM_ITALIC;
1315 cf2.dwEffects = 0;
1316 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1318 /* check CFE_UNDERLINE is clear and bUnderlineType is CFU_UNDERLINE */
1319 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1320 cf2.cbSize = sizeof(CHARFORMAT2A);
1321 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1322 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1323 "got %08lx\n", cf2.dwMask);
1324 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08lx\n", cf2.dwEffects);
1325 ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1327 /* simply touching bUnderlineType will toggle CFE_UNDERLINE */
1328 cf2.dwMask = CFM_UNDERLINETYPE;
1329 cf2.bUnderlineType = CFU_UNDERLINE;
1330 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1331 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1332 cf2.cbSize = sizeof(CHARFORMAT2A);
1333 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1334 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1335 "got %08lx\n", cf2.dwMask);
1336 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08lx\n", cf2.dwEffects);
1337 ok(cf2.bUnderlineType == CFU_UNDERLINE, "got %x\n", cf2.bUnderlineType);
1339 /* setting bUnderline to CFU_UNDERLINENONE clears CFE_UNDERLINE */
1340 cf2.dwMask = CFM_UNDERLINETYPE;
1341 cf2.bUnderlineType = CFU_UNDERLINENONE;
1342 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1343 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1344 cf2.cbSize = sizeof(CHARFORMAT2A);
1345 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1346 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1347 "got %08lx\n", cf2.dwMask);
1348 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08lx\n", cf2.dwEffects);
1349 ok(cf2.bUnderlineType == CFU_UNDERLINENONE, "got %x\n", cf2.bUnderlineType);
1351 /* another underline type also sets CFE_UNDERLINE */
1352 cf2.dwMask = CFM_UNDERLINETYPE;
1353 cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1354 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1355 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1356 cf2.cbSize = sizeof(CHARFORMAT2A);
1357 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1358 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1359 "got %08lx\n", cf2.dwMask);
1360 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08lx\n", cf2.dwEffects);
1361 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1363 /* However explicitly clearing CFE_UNDERLINE results in it remaining cleared */
1364 cf2.dwMask = CFM_UNDERLINETYPE | CFM_UNDERLINE;
1365 cf2.bUnderlineType = CFU_UNDERLINEDOUBLE;
1366 cf2.dwEffects = 0;
1367 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1368 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1369 cf2.cbSize = sizeof(CHARFORMAT2A);
1370 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1371 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1372 "got %08lx\n", cf2.dwMask);
1373 ok(!(cf2.dwEffects & CFE_UNDERLINE), "got %08lx\n", cf2.dwEffects);
1374 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1376 /* And turing it back on again by just setting CFE_UNDERLINE */
1377 cf2.dwMask = CFM_UNDERLINE;
1378 cf2.dwEffects = CFE_UNDERLINE;
1379 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1380 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1381 cf2.cbSize = sizeof(CHARFORMAT2A);
1382 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1383 ok((cf2.dwMask & (CFM_UNDERLINE | CFM_UNDERLINETYPE)) == (CFM_UNDERLINE | CFM_UNDERLINETYPE),
1384 "got %08lx\n", cf2.dwMask);
1385 ok(cf2.dwEffects & CFE_UNDERLINE, "got %08lx\n", cf2.dwEffects);
1386 ok(cf2.bUnderlineType == CFU_UNDERLINEDOUBLE, "got %x\n", cf2.bUnderlineType);
1388 /* Check setting CFM_ALL2/CFM_EFFECTS2 in CHARFORMAT(A/W). */
1389 memset(&cf1a, 0, sizeof(CHARFORMATA));
1390 memset(&cf1w, 0, sizeof(CHARFORMATW));
1391 cf1a.cbSize = sizeof(CHARFORMATA);
1392 cf1w.cbSize = sizeof(CHARFORMATW);
1393 cf1a.dwMask = cf1w.dwMask = CFM_ALL2;
1394 cf1a.dwEffects = cf1w.dwEffects = CFM_EFFECTS2;
1395 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1a);
1396 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1a);
1397 /* flags only valid for CHARFORMAT2 should be masked out */
1398 ok((cf1a.dwMask & (CFM_ALL2 & ~CFM_ALL)) == 0, "flags were not masked out\n");
1399 ok((cf1a.dwEffects & (CFM_EFFECTS2 & ~CFM_EFFECTS)) == 0, "flags were not masked out\n");
1400 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1w);
1401 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf1w);
1402 ok((cf1w.dwMask & (CFM_ALL2 & ~CFM_ALL)) == 0, "flags were not masked out\n");
1403 ok((cf1w.dwEffects & (CFM_EFFECTS2 & ~CFM_EFFECTS)) == 0, "flags were not masked out\n");
1405 DestroyWindow(hwndRichEdit);
1408 /* As the clipboard is a shared resource, it happens (on Windows) that the WM_PASTE
1409 * is a no-op; likely because another app has opened the clipboard for inspection.
1410 * In this case, WM_PASTE does nothing, and doesn't return an error code.
1411 * So retry pasting a couple of times.
1412 * Don't use this function if the paste operation shouldn't change the content of the
1413 * editor (clipboard is empty without selection, edit control is read only...).
1414 * Also impact on undo stack is not managed.
1416 #define send_paste(a) _send_paste(__LINE__, (a))
1417 static void _send_paste(unsigned int line, HWND wnd)
1419 int retries;
1421 SendMessageA(wnd, EM_SETMODIFY, FALSE, 0);
1423 for (retries = 0; retries < 7; retries++)
1425 if (retries) Sleep(15);
1426 SendMessageA(wnd, WM_PASTE, 0, 0);
1427 if (SendMessageA(wnd, EM_GETMODIFY, 0, 0)) return;
1429 ok_(__FILE__, line)(0, "Failed to paste clipboard content\n");
1431 char classname[256];
1432 HWND clipwnd = GetOpenClipboardWindow();
1433 /* Provide a hint as to the source of interference:
1434 * - The class name would typically be CLIPBRDWNDCLASS if the
1435 * clipboard was opened by a Windows application using the
1436 * ole32 API.
1437 * - And it would be __wine_clipboard_manager if it was opened in
1438 * response to a native application.
1440 GetClassNameA(clipwnd, classname, ARRAY_SIZE(classname));
1441 trace("%p (%s) opened the clipboard\n", clipwnd, classname);
1445 static void test_EM_SETTEXTMODE(void)
1447 HWND hwndRichEdit = new_richedit(NULL);
1448 CHARFORMAT2A cf2, cf2test;
1449 unsigned int len;
1450 CHARRANGE cr;
1451 int rc = 0;
1453 /*Attempt to use mutually exclusive modes*/
1454 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1455 ok(rc == E_INVALIDARG,
1456 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1458 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1459 /*Insert text into the control*/
1461 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1463 /*Attempt to change the control to plain text mode*/
1464 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1465 ok(rc == E_UNEXPECTED,
1466 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1468 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1469 If rich text is pasted, it should have the same formatting as the rest
1470 of the text in the control*/
1472 /*Italicize the text
1473 *NOTE: If the default text was already italicized, the test will simply
1474 reverse; in other words, it will copy a regular "wine" into a plain
1475 text window that uses an italicized format*/
1476 cf2.cbSize = sizeof(CHARFORMAT2A);
1477 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1479 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1480 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1482 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1483 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1485 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1486 however, SCF_ALL has been implemented*/
1487 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1488 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1490 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1491 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1493 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1495 /*Select the string "wine"*/
1496 cr.cpMin = 0;
1497 cr.cpMax = 4;
1498 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1500 /*Copy the italicized "wine" to the clipboard*/
1501 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1503 /*Reset the formatting to default*/
1504 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1505 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1506 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1508 /*Clear the text in the control*/
1509 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1511 /*Switch to Plain Text Mode*/
1512 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1513 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1515 /*Input "wine" again in normal format*/
1516 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1518 /*Paste the italicized "wine" into the control*/
1519 send_paste(hwndRichEdit);
1521 len = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1522 ok(len == 8 /*winewine*/, "Unexpected text length %u\n", len);
1524 /*Select a character from the first "wine" string*/
1525 cr.cpMin = 2;
1526 cr.cpMax = 3;
1527 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1529 /*Retrieve its formatting*/
1530 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1532 /*Select a character from the second "wine" string*/
1533 cr.cpMin = 5;
1534 cr.cpMax = 6;
1535 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1537 /*Retrieve its formatting*/
1538 cf2test.cbSize = sizeof(CHARFORMAT2A);
1539 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1541 /*Compare the two formattings*/
1542 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1543 "two formats found in plain text mode - cf2.dwEffects: %lx cf2test.dwEffects: %lx\n",
1544 cf2.dwEffects, cf2test.dwEffects);
1545 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1546 printing "wine" in the current format(normal)
1547 pasting "wine" from the clipboard(italicized)
1548 comparing the two formats(should differ)*/
1550 /*Attempt to switch with text in control*/
1551 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1552 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1554 /*Clear control*/
1555 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1557 /*Switch into Rich Text mode*/
1558 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1559 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1561 /*Print "wine" in normal formatting into the control*/
1562 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1564 /*Paste italicized "wine" into the control*/
1565 send_paste(hwndRichEdit);
1567 /*Select text from the first "wine" string*/
1568 cr.cpMin = 1;
1569 cr.cpMax = 3;
1570 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1572 /*Retrieve its formatting*/
1573 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1575 /*Select text from the second "wine" string*/
1576 cr.cpMin = 6;
1577 cr.cpMax = 7;
1578 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1580 /*Retrieve its formatting*/
1581 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1583 /*Test that the two formattings are not the same*/
1584 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1585 "expected different formats - cf2.dwMask: %lx, cf2test.dwMask: %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
1586 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1588 DestroyWindow(hwndRichEdit);
1591 static void test_SETPARAFORMAT(void)
1593 HWND hwndRichEdit = new_richedit(NULL);
1594 PARAFORMAT2 fmt;
1595 HRESULT ret;
1596 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1597 fmt.cbSize = sizeof(PARAFORMAT2);
1598 fmt.dwMask = PFM_ALIGNMENT;
1599 fmt.wAlignment = PFA_LEFT;
1601 ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1602 ok(ret != 0, "expected non-zero got %ld\n", ret);
1604 fmt.cbSize = sizeof(PARAFORMAT2);
1605 fmt.dwMask = -1;
1606 ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1607 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1608 * between richedit different native builds of riched20.dll
1609 * used on different Windows versions. */
1610 ret &= ~PFM_TABLEROWDELIMITER;
1611 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1613 ok(ret == expectedMask, "expected %lx got %lx\n", expectedMask, ret);
1614 ok(fmt.dwMask == expectedMask, "expected %lx got %lx\n", expectedMask, fmt.dwMask);
1616 /* Test some other paraformat field defaults */
1617 ok( fmt.wNumbering == 0, "got %d\n", fmt.wNumbering );
1618 ok( fmt.wNumberingStart == 0, "got %d\n", fmt.wNumberingStart );
1619 ok( fmt.wNumberingStyle == 0, "got %04x\n", fmt.wNumberingStyle );
1620 ok( fmt.wNumberingTab == 0, "got %d\n", fmt.wNumberingTab );
1622 DestroyWindow(hwndRichEdit);
1625 static void test_TM_PLAINTEXT(void)
1627 /*Tests plain text properties*/
1629 HWND hwndRichEdit = new_richedit(NULL);
1630 CHARFORMAT2A cf2, cf2test;
1631 CHARRANGE cr;
1632 int rc = 0;
1634 /*Switch to plain text mode*/
1636 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1637 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1639 /*Fill control with text*/
1641 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1643 /*Select some text and bold it*/
1645 cr.cpMin = 10;
1646 cr.cpMax = 20;
1647 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1648 cf2.cbSize = sizeof(CHARFORMAT2A);
1649 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1651 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1652 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1654 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1655 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1657 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1658 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1660 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1661 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1663 /*Get the formatting of those characters*/
1665 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1667 /*Get the formatting of some other characters*/
1668 cf2test.cbSize = sizeof(CHARFORMAT2A);
1669 cr.cpMin = 21;
1670 cr.cpMax = 30;
1671 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1672 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1674 /*Test that they are the same as plain text allows only one formatting*/
1676 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1677 "two selections' formats differ - cf2.dwMask: %lx, cf2test.dwMask %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
1678 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1680 /*Fill the control with a "wine" string, which when inserted will be bold*/
1682 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1684 /*Copy the bolded "wine" string*/
1686 cr.cpMin = 0;
1687 cr.cpMax = 4;
1688 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1689 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1691 /*Swap back to rich text*/
1693 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1694 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1696 /*Set the default formatting to bold italics*/
1698 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1699 cf2.dwMask |= CFM_ITALIC;
1700 cf2.dwEffects ^= CFE_ITALIC;
1701 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1702 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1704 /*Set the text in the control to "wine", which will be bold and italicized*/
1706 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1708 /*Paste the plain text "wine" string, which should take the insert
1709 formatting, which at the moment is bold italics*/
1711 send_paste(hwndRichEdit);
1713 /*Select the first "wine" string and retrieve its formatting*/
1715 cr.cpMin = 1;
1716 cr.cpMax = 3;
1717 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1718 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1720 /*Select the second "wine" string and retrieve its formatting*/
1722 cr.cpMin = 5;
1723 cr.cpMax = 7;
1724 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1725 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1727 /*Compare the two formattings. They should be the same.*/
1729 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1730 "Copied text retained formatting - cf2.dwMask: %lx, cf2test.dwMask: %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
1731 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1732 DestroyWindow(hwndRichEdit);
1735 static void test_WM_GETTEXT(void)
1737 HWND hwndRichEdit = new_richedit(NULL);
1738 static const char text[] = "Hello. My name is RichEdit!";
1739 static const char text2[] = "Hello. My name is RichEdit!\r";
1740 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1741 char buffer[1024] = {0};
1742 int result;
1744 /* Baseline test with normal-sized buffer */
1745 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1746 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1747 ok(result == lstrlenA(buffer),
1748 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1749 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1750 result = strcmp(buffer,text);
1751 ok(result == 0,
1752 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1754 /* Test for returned value of WM_GETTEXTLENGTH */
1755 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1756 ok(result == lstrlenA(text),
1757 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1758 result, lstrlenA(text));
1760 /* Test for behavior in overflow case */
1761 memset(buffer, 0, 1024);
1762 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1763 ok(result == 0 ||
1764 result == lstrlenA(text) - 1, /* XP, win2k3 */
1765 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1766 result = strcmp(buffer,text);
1767 if (result)
1768 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1769 ok(result == 0,
1770 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1772 /* Baseline test with normal-sized buffer and carriage return */
1773 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1774 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1775 ok(result == lstrlenA(buffer),
1776 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1777 result = strcmp(buffer,text2_after);
1778 ok(result == 0,
1779 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1781 /* Test for returned value of WM_GETTEXTLENGTH */
1782 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1783 ok(result == lstrlenA(text2_after),
1784 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1785 result, lstrlenA(text2_after));
1787 /* Test for behavior of CRLF conversion in case of overflow */
1788 memset(buffer, 0, 1024);
1789 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1790 ok(result == 0 ||
1791 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1792 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1793 result = strcmp(buffer,text2);
1794 if (result)
1795 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1796 ok(result == 0,
1797 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1799 DestroyWindow(hwndRichEdit);
1802 static void test_EM_GETTEXTRANGE(void)
1804 HWND hwndRichEdit = new_richedit(NULL);
1805 const char * text1 = "foo bar\r\nfoo bar";
1806 const char * text2 = "foo bar\rfoo bar";
1807 const char * expect = "bar\rfoo";
1808 char buffer[1024] = {0};
1809 LRESULT result;
1810 TEXTRANGEA textRange;
1812 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1814 textRange.lpstrText = buffer;
1815 textRange.chrg.cpMin = 4;
1816 textRange.chrg.cpMax = 11;
1817 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1818 ok(result == 7, "EM_GETTEXTRANGE returned %Id\n", result);
1819 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1821 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1823 textRange.lpstrText = buffer;
1824 textRange.chrg.cpMin = 4;
1825 textRange.chrg.cpMax = 11;
1826 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1827 ok(result == 7, "EM_GETTEXTRANGE returned %Id\n", result);
1828 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1830 /* cpMax of text length is used instead of -1 in this case */
1831 textRange.lpstrText = buffer;
1832 textRange.chrg.cpMin = 0;
1833 textRange.chrg.cpMax = -1;
1834 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1835 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %Id\n", result);
1836 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1838 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1839 textRange.lpstrText = buffer;
1840 textRange.chrg.cpMin = -1;
1841 textRange.chrg.cpMax = 1;
1842 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1843 ok(result == 0, "EM_GETTEXTRANGE returned %Id\n", result);
1844 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1846 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1847 textRange.lpstrText = buffer;
1848 textRange.chrg.cpMin = 1;
1849 textRange.chrg.cpMax = -1;
1850 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1851 ok(result == 0, "EM_GETTEXTRANGE returned %Id\n", result);
1852 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1854 /* no end character is copied if cpMax - cpMin < 0 */
1855 textRange.lpstrText = buffer;
1856 textRange.chrg.cpMin = 5;
1857 textRange.chrg.cpMax = 5;
1858 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1859 ok(result == 0, "EM_GETTEXTRANGE returned %Id\n", result);
1860 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1862 /* cpMax of text length is used if cpMax > text length*/
1863 textRange.lpstrText = buffer;
1864 textRange.chrg.cpMin = 0;
1865 textRange.chrg.cpMax = 1000;
1866 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1867 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %Id\n", result);
1868 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1870 /* Test with multibyte character */
1871 if (!is_lang_japanese)
1872 skip("Skip multibyte character tests on non-Japanese platform\n");
1873 else
1875 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1876 textRange.chrg.cpMin = 4;
1877 textRange.chrg.cpMax = 8;
1878 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1879 ok(result == 5, "EM_GETTEXTRANGE returned %Id\n", result);
1880 ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1883 DestroyWindow(hwndRichEdit);
1886 static void test_EM_GETSELTEXT(void)
1888 HWND hwndRichEdit = new_richedit(NULL);
1889 const char * text1 = "foo bar\r\nfoo bar";
1890 const char * text2 = "foo bar\rfoo bar";
1891 const char * expect = "bar\rfoo";
1892 char buffer[1024] = {0};
1893 LRESULT result;
1894 BOOL bad_getsel;
1895 DWORD gle;
1897 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
1898 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
1899 SetLastError(0xdeadbeef);
1900 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1901 gle = GetLastError();
1902 ok((result > 0 && gle == 0xdeadbeef) ||
1903 broken(result == 0 && gle == ERROR_INVALID_PARAMETER /* Hindi */),
1904 "EM_GETSELTEXT returned %Id gle=%lu\n", result, gle);
1905 bad_getsel = (gle != 0xdeadbeef);
1906 if (bad_getsel)
1907 trace("EM_GETSELTEXT is broken, some tests will be ignored\n");
1909 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1911 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1912 SetLastError(0xdeadbeef);
1913 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1914 gle = GetLastError();
1915 ok(result == 7 || broken(bad_getsel && result == 0),
1916 "EM_GETSELTEXT returned %Id gle=%lu\n", result, gle);
1917 ok(!strcmp(expect, buffer) || broken(bad_getsel && !*buffer),
1918 "EM_GETSELTEXT filled %s gle=%lu\n", buffer, gle);
1920 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1922 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1923 SetLastError(0xdeadbeef);
1924 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1925 ok(result == 7 || broken(bad_getsel && result == 0),
1926 "EM_GETSELTEXT returned %Id gle=%lu\n", result, gle);
1927 ok(!strcmp(expect, buffer) || broken(bad_getsel && !*buffer),
1928 "EM_GETSELTEXT filled %s gle=%lu\n", buffer, gle);
1930 /* Test with multibyte character */
1931 if (!is_lang_japanese)
1932 skip("Skip multibyte character tests on non-Japanese platform\n");
1933 else
1935 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1936 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1937 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1938 ok(result == 5, "EM_GETSELTEXT returned %Id\n", result);
1939 ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", buffer);
1942 DestroyWindow(hwndRichEdit);
1945 /* FIXME: need to test unimplemented options and robustly test wparam */
1946 static void test_EM_SETOPTIONS(void)
1948 HWND hwndRichEdit;
1949 static const char text[] = "Hello. My name is RichEdit!";
1950 char buffer[1024] = {0};
1951 DWORD dwStyle, options, oldOptions;
1952 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1953 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1954 ES_SELECTIONBAR|ES_VERTICAL;
1956 /* Test initial options. */
1957 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1958 0, 0, 200, 60, NULL, NULL,
1959 hmoduleRichEdit, NULL);
1960 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1961 RICHEDIT_CLASS20A, (int) GetLastError());
1962 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1963 ok(options == 0, "Incorrect initial options %lx\n", options);
1964 DestroyWindow(hwndRichEdit);
1966 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1967 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1968 0, 0, 200, 60, NULL, NULL,
1969 hmoduleRichEdit, NULL);
1970 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1971 RICHEDIT_CLASS20A, (int) GetLastError());
1972 disable_beep( hwndRichEdit );
1973 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1974 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1975 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1976 "Incorrect initial options %lx\n", options);
1978 /* NEGATIVE TESTING - NO OPTIONS SET */
1979 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1980 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1982 /* testing no readonly by sending 'a' to the control*/
1983 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1984 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1985 ok(buffer[0]=='a',
1986 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1987 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1989 /* READONLY - sending 'a' to the control */
1990 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1991 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1992 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1993 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1994 ok(buffer[0]==text[0],
1995 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1997 /* EM_SETOPTIONS changes the window style, but changing the
1998 * window style does not change the options. */
1999 dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
2000 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
2001 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
2002 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
2003 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
2004 /* Confirm that the text is still read only. */
2005 SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
2006 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
2007 ok(buffer[0]==text[0],
2008 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
2010 oldOptions = options;
2011 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
2012 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
2013 ok(options == oldOptions,
2014 "Options set by SetWindowLong (%lx -> %lx)\n", oldOptions, options);
2016 DestroyWindow(hwndRichEdit);
2019 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
2021 CHARFORMAT2A text_format;
2022 text_format.cbSize = sizeof(text_format);
2023 SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
2024 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
2025 return (text_format.dwEffects & CFE_LINK) != 0;
2028 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
2030 BOOL link_present = FALSE;
2032 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
2033 if (is_url)
2034 { /* control text is url; should get CFE_LINK */
2035 ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
2037 else
2039 ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
2043 static HWND new_static_wnd(HWND parent) {
2044 return new_window("Static", 0, parent);
2047 static void test_EM_AUTOURLDETECT(void)
2049 /* DO NOT change the properties of the first two elements. To shorten the
2050 tests, all tests after WM_SETTEXT test just the first two elements -
2051 one non-URL and one URL */
2052 struct urls_s {
2053 const char *text;
2054 BOOL is_url;
2055 } urls[12] = {
2056 {"winehq.org", FALSE},
2057 {"http://www.winehq.org", TRUE},
2058 {"http//winehq.org", FALSE},
2059 {"ww.winehq.org", FALSE},
2060 {"www.winehq.org", TRUE},
2061 {"ftp://192.168.1.1", TRUE},
2062 {"ftp//192.168.1.1", FALSE},
2063 {"mailto:your@email.com", TRUE},
2064 {"prospero:prosperoserver", TRUE},
2065 {"telnet:test", TRUE},
2066 {"news:newserver", TRUE},
2067 {"wais:waisserver", TRUE}
2070 int i, j;
2071 int urlRet=-1;
2072 HWND hwndRichEdit, parent;
2074 /* All of the following should cause the URL to be detected */
2075 const char * templates_delim[] = {
2076 "This is some text with X on it",
2077 "This is some text with (X) on it",
2078 "This is some text with X\r on it",
2079 "This is some text with ---X--- on it",
2080 "This is some text with \"X\" on it",
2081 "This is some text with 'X' on it",
2082 "This is some text with 'X' on it",
2083 "This is some text with :X: on it",
2085 "This text ends with X",
2087 "This is some text with X) on it",
2088 "This is some text with X--- on it",
2089 "This is some text with X\" on it",
2090 "This is some text with X' on it",
2091 "This is some text with X: on it",
2093 "This is some text with (X on it",
2094 "This is some text with \rX on it",
2095 "This is some text with ---X on it",
2096 "This is some text with \"X on it",
2097 "This is some text with 'X on it",
2098 "This is some text with :X on it",
2100 /* None of these should cause the URL to be detected */
2101 const char * templates_non_delim[] = {
2102 "This is some text with |X| on it",
2103 "This is some text with *X* on it",
2104 "This is some text with /X/ on it",
2105 "This is some text with +X+ on it",
2106 "This is some text with %X% on it",
2107 "This is some text with #X# on it",
2108 "This is some text with @X@ on it",
2109 "This is some text with \\X\\ on it",
2110 "This is some text with |X on it",
2111 "This is some text with *X on it",
2112 "This is some text with /X on it",
2113 "This is some text with +X on it",
2114 "This is some text with %X on it",
2115 "This is some text with #X on it",
2116 "This is some text with @X on it",
2117 "This is some text with \\X on it",
2118 "This is some text with _X on it",
2120 /* All of these cause the URL detection to be extended by one more byte,
2121 thus demonstrating that the tested character is considered as part
2122 of the URL. */
2123 const char * templates_xten_delim[] = {
2124 "This is some text with X| on it",
2125 "This is some text with X* on it",
2126 "This is some text with X/ on it",
2127 "This is some text with X+ on it",
2128 "This is some text with X% on it",
2129 "This is some text with X# on it",
2130 "This is some text with X@ on it",
2131 "This is some text with X\\ on it",
2132 "This is some text with X_ on it",
2134 /* These delims act as neutral breaks. Whether the url is ended
2135 or not depends on the next non-neutral character. We'll test
2136 with Y unchanged, in which case the url should include the
2137 deliminator and the Y. We'll also test with the Y changed
2138 to a space, in which case the url stops before the
2139 deliminator. */
2140 const char * templates_neutral_delim[] = {
2141 "This is some text with X-Y on it",
2142 "This is some text with X--Y on it",
2143 "This is some text with X!Y on it",
2144 "This is some text with X[Y on it",
2145 "This is some text with X]Y on it",
2146 "This is some text with X{Y on it",
2147 "This is some text with X}Y on it",
2148 "This is some text with X(Y on it",
2149 "This is some text with X)Y on it",
2150 "This is some text with X\"Y on it",
2151 "This is some text with X;Y on it",
2152 "This is some text with X:Y on it",
2153 "This is some text with X'Y on it",
2154 "This is some text with X?Y on it",
2155 "This is some text with X<Y on it",
2156 "This is some text with X>Y on it",
2157 "This is some text with X.Y on it",
2158 "This is some text with X,Y on it",
2160 char buffer[1024];
2162 parent = new_static_wnd(NULL);
2163 hwndRichEdit = new_richedit(parent);
2164 /* Try and pass EM_AUTOURLDETECT some test wParam values */
2165 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2166 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
2167 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
2168 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
2169 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
2170 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
2171 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
2172 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
2173 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
2174 /* for each url, check the text to see if CFE_LINK effect is present */
2175 for (i = 0; i < ARRAY_SIZE(urls); i++) {
2177 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
2178 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2179 check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
2181 /* Link detection should happen immediately upon WM_SETTEXT */
2182 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2183 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
2184 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
2186 DestroyWindow(hwndRichEdit);
2188 /* Test detection of URLs within normal text - WM_SETTEXT case. */
2189 for (i = 0; i < ARRAY_SIZE(urls); i++) {
2190 hwndRichEdit = new_richedit(parent);
2192 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2193 char * at_pos;
2194 int at_offset;
2195 int end_offset;
2197 at_pos = strchr(templates_delim[j], 'X');
2198 at_offset = at_pos - templates_delim[j];
2199 memcpy(buffer, templates_delim[j], at_offset);
2200 buffer[at_offset] = '\0';
2201 strcat(buffer, urls[i].text);
2202 strcat(buffer, templates_delim[j] + at_offset + 1);
2203 end_offset = at_offset + strlen(urls[i].text);
2205 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2206 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2208 /* This assumes no templates start with the URL itself, and that they
2209 have at least two characters before the URL text */
2210 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2211 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2212 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2213 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2214 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2215 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2217 if (urls[i].is_url)
2219 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2220 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2221 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2222 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2224 else
2226 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2227 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2228 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2229 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2231 if (buffer[end_offset] != '\0')
2233 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2234 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2235 if (buffer[end_offset +1] != '\0')
2237 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2238 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2243 for (j = 0; j < ARRAY_SIZE(templates_non_delim); j++) {
2244 char * at_pos;
2245 int at_offset;
2246 int end_offset;
2248 at_pos = strchr(templates_non_delim[j], 'X');
2249 at_offset = at_pos - templates_non_delim[j];
2250 memcpy(buffer, templates_non_delim[j], at_offset);
2251 buffer[at_offset] = '\0';
2252 strcat(buffer, urls[i].text);
2253 strcat(buffer, templates_non_delim[j] + at_offset + 1);
2254 end_offset = at_offset + strlen(urls[i].text);
2256 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2257 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2259 /* This assumes no templates start with the URL itself, and that they
2260 have at least two characters before the URL text */
2261 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2262 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2263 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2264 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2265 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2266 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2268 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2269 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2270 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2271 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2272 if (buffer[end_offset] != '\0')
2274 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2275 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2276 if (buffer[end_offset +1] != '\0')
2278 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2279 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2284 for (j = 0; j < ARRAY_SIZE(templates_xten_delim); j++) {
2285 char * at_pos;
2286 int at_offset;
2287 int end_offset;
2289 at_pos = strchr(templates_xten_delim[j], 'X');
2290 at_offset = at_pos - templates_xten_delim[j];
2291 memcpy(buffer, templates_xten_delim[j], at_offset);
2292 buffer[at_offset] = '\0';
2293 strcat(buffer, urls[i].text);
2294 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
2295 end_offset = at_offset + strlen(urls[i].text);
2297 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2298 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2300 /* This assumes no templates start with the URL itself, and that they
2301 have at least two characters before the URL text */
2302 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2303 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2304 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2305 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2306 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2307 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2309 if (urls[i].is_url)
2311 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2312 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2313 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2314 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2315 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2316 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2318 else
2320 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2321 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2322 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2323 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2324 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2325 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2327 if (buffer[end_offset +1] != '\0')
2329 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2330 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
2331 if (buffer[end_offset +2] != '\0')
2333 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2334 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2339 for (j = 0; j < ARRAY_SIZE(templates_neutral_delim); j++) {
2340 char * at_pos, * end_pos;
2341 int at_offset;
2342 int end_offset;
2344 if (!urls[i].is_url) continue;
2346 at_pos = strchr(templates_neutral_delim[j], 'X');
2347 at_offset = at_pos - templates_neutral_delim[j];
2348 memcpy(buffer, templates_neutral_delim[j], at_offset);
2349 buffer[at_offset] = '\0';
2350 strcat(buffer, urls[i].text);
2351 strcat(buffer, templates_neutral_delim[j] + at_offset + 1);
2353 end_pos = strchr(buffer, 'Y');
2354 end_offset = end_pos - buffer;
2356 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2357 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2359 /* This assumes no templates start with the URL itself, and that they
2360 have at least two characters before the URL text */
2361 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2362 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2363 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2364 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2365 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2366 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2368 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2369 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2370 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2371 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2372 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2373 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2375 *end_pos = ' ';
2377 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2378 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2380 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2381 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2382 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2383 "CFE_LINK set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2384 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2385 "CFE_LINK set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2388 DestroyWindow(hwndRichEdit);
2389 hwndRichEdit = NULL;
2392 /* Test detection of URLs within normal text - WM_CHAR case. */
2393 /* Test only the first two URL examples for brevity */
2394 for (i = 0; i < 2; i++) {
2395 hwndRichEdit = new_richedit(parent);
2397 /* Also for brevity, test only the first three delimiters */
2398 for (j = 0; j < 3; j++) {
2399 char * at_pos;
2400 int at_offset;
2401 int end_offset;
2402 int u, v;
2404 at_pos = strchr(templates_delim[j], 'X');
2405 at_offset = at_pos - templates_delim[j];
2406 end_offset = at_offset + strlen(urls[i].text);
2408 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2409 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2410 for (u = 0; templates_delim[j][u]; u++) {
2411 if (templates_delim[j][u] == '\r') {
2412 simulate_typing_characters(hwndRichEdit, "\r");
2413 } else if (templates_delim[j][u] != 'X') {
2414 SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
2415 } else {
2416 for (v = 0; urls[i].text[v]; v++) {
2417 SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
2421 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2423 /* This assumes no templates start with the URL itself, and that they
2424 have at least two characters before the URL text */
2425 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2426 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2427 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2428 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2429 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2430 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2432 if (urls[i].is_url)
2434 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2435 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2436 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2437 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2439 else
2441 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2442 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2443 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2444 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2446 if (buffer[end_offset] != '\0')
2448 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2449 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2450 if (buffer[end_offset +1] != '\0')
2452 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2453 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2457 /* The following will insert a paragraph break after the first character
2458 of the URL candidate, thus breaking the URL. It is expected that the
2459 CFE_LINK attribute should break across both pieces of the URL */
2460 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2461 simulate_typing_characters(hwndRichEdit, "\r");
2462 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2464 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2465 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2466 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2467 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2468 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2469 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2471 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2472 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2473 /* end_offset moved because of paragraph break */
2474 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2475 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2476 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2477 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2479 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2480 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2481 if (buffer[end_offset +2] != '\0')
2483 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2484 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2488 /* The following will remove the just-inserted paragraph break, thus
2489 restoring the URL */
2490 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2491 simulate_typing_characters(hwndRichEdit, "\b");
2492 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2494 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2495 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2496 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2497 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2498 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2499 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2501 if (urls[i].is_url)
2503 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2504 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2505 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2506 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2508 else
2510 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2511 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2512 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2513 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2515 if (buffer[end_offset] != '\0')
2517 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2518 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2519 if (buffer[end_offset +1] != '\0')
2521 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2522 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2526 DestroyWindow(hwndRichEdit);
2527 hwndRichEdit = NULL;
2530 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2531 /* Test just the first two URL examples for brevity */
2532 for (i = 0; i < 2; i++) {
2533 SETTEXTEX st;
2535 hwndRichEdit = new_richedit(parent);
2537 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2538 be detected:
2539 1) Set entire text, a la WM_SETTEXT
2540 2) Set a selection of the text to the URL
2541 3) Set a portion of the text at a time, which eventually results in
2542 an URL
2543 All of them should give equivalent results
2546 /* Set entire text in one go, like WM_SETTEXT */
2547 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2548 char * at_pos;
2549 int at_offset;
2550 int end_offset;
2552 st.codepage = CP_ACP;
2553 st.flags = ST_DEFAULT;
2555 at_pos = strchr(templates_delim[j], 'X');
2556 at_offset = at_pos - templates_delim[j];
2557 memcpy(buffer, templates_delim[j], at_offset);
2558 buffer[at_offset] = '\0';
2559 strcat(buffer, urls[i].text);
2560 strcat(buffer, templates_delim[j] + at_offset + 1);
2561 end_offset = at_offset + strlen(urls[i].text);
2563 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2564 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2566 /* This assumes no templates start with the URL itself, and that they
2567 have at least two characters before the URL text */
2568 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2569 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2570 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2571 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2572 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2573 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2575 if (urls[i].is_url)
2577 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2578 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2579 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2580 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2582 else
2584 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2585 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2586 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2587 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2589 if (buffer[end_offset] != '\0')
2591 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2592 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2593 if (buffer[end_offset +1] != '\0')
2595 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2596 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2601 /* Set selection with X to the URL */
2602 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2603 char * at_pos;
2604 int at_offset;
2605 int end_offset;
2607 at_pos = strchr(templates_delim[j], 'X');
2608 at_offset = at_pos - templates_delim[j];
2609 end_offset = at_offset + strlen(urls[i].text);
2611 st.codepage = CP_ACP;
2612 st.flags = ST_DEFAULT;
2613 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2614 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2615 st.flags = ST_SELECTION;
2616 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2617 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2618 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2620 /* This assumes no templates start with the URL itself, and that they
2621 have at least two characters before the URL text */
2622 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2623 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2624 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2625 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2626 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2627 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2629 if (urls[i].is_url)
2631 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2632 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2633 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2634 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2636 else
2638 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2639 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2640 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2641 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2643 if (buffer[end_offset] != '\0')
2645 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2646 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2647 if (buffer[end_offset +1] != '\0')
2649 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2650 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2655 /* Set selection with X to the first character of the URL, then the rest */
2656 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2657 char * at_pos;
2658 int at_offset;
2659 int end_offset;
2661 at_pos = strchr(templates_delim[j], 'X');
2662 at_offset = at_pos - templates_delim[j];
2663 end_offset = at_offset + strlen(urls[i].text);
2665 strcpy(buffer, "YY");
2666 buffer[0] = urls[i].text[0];
2668 st.codepage = CP_ACP;
2669 st.flags = ST_DEFAULT;
2670 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2671 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2672 st.flags = ST_SELECTION;
2673 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2674 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2675 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2676 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2677 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2679 /* This assumes no templates start with the URL itself, and that they
2680 have at least two characters before the URL text */
2681 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2682 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2683 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2684 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2685 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2686 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2688 if (urls[i].is_url)
2690 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2691 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2692 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2693 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2695 else
2697 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2698 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2699 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2700 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2702 if (buffer[end_offset] != '\0')
2704 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2705 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2706 if (buffer[end_offset +1] != '\0')
2708 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2714 DestroyWindow(hwndRichEdit);
2715 hwndRichEdit = NULL;
2718 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2719 /* Test just the first two URL examples for brevity */
2720 for (i = 0; i < 2; i++) {
2721 hwndRichEdit = new_richedit(parent);
2723 /* Set selection with X to the URL */
2724 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2725 char * at_pos;
2726 int at_offset;
2727 int end_offset;
2729 at_pos = strchr(templates_delim[j], 'X');
2730 at_offset = at_pos - templates_delim[j];
2731 end_offset = at_offset + strlen(urls[i].text);
2733 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2734 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2735 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2736 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2737 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2739 /* This assumes no templates start with the URL itself, and that they
2740 have at least two characters before the URL text */
2741 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2742 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2743 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2744 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2745 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2746 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2748 if (urls[i].is_url)
2750 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2751 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2752 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2753 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2755 else
2757 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2758 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2759 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2760 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2762 if (buffer[end_offset] != '\0')
2764 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2765 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2766 if (buffer[end_offset +1] != '\0')
2768 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2769 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2774 /* Set selection with X to the first character of the URL, then the rest */
2775 for (j = 0; j < ARRAY_SIZE(templates_delim); j++) {
2776 char * at_pos;
2777 int at_offset;
2778 int end_offset;
2780 at_pos = strchr(templates_delim[j], 'X');
2781 at_offset = at_pos - templates_delim[j];
2782 end_offset = at_offset + strlen(urls[i].text);
2784 strcpy(buffer, "YY");
2785 buffer[0] = urls[i].text[0];
2787 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2788 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2789 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2790 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2791 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2792 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2793 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2795 /* This assumes no templates start with the URL itself, and that they
2796 have at least two characters before the URL text */
2797 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2798 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2799 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2800 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2801 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2802 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2804 if (urls[i].is_url)
2806 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2807 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2808 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2809 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2811 else
2813 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2814 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2815 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2816 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2818 if (buffer[end_offset] != '\0')
2820 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2821 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2822 if (buffer[end_offset +1] != '\0')
2824 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2825 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2830 DestroyWindow(hwndRichEdit);
2831 hwndRichEdit = NULL;
2834 DestroyWindow(parent);
2837 static void test_EM_SCROLL(void)
2839 int i, j;
2840 int r; /* return value */
2841 int expr; /* expected return value */
2842 HWND hwndRichEdit = new_richedit(NULL);
2843 int y_before, y_after; /* units of lines of text */
2845 /* test a richedit box containing a single line of text */
2846 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2847 expr = 0x00010000;
2848 for (i = 0; i < 4; i++) {
2849 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2851 r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2852 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2853 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2854 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2855 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2856 "(i == %d)\n", y_after, i);
2860 * test a richedit box that will scroll. There are two general
2861 * cases: the case without any long lines and the case with a long
2862 * line.
2864 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2865 if (i == 0)
2866 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2867 else
2868 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2869 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2870 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2871 "LONG LINE \nb\nc\nd\ne");
2872 for (j = 0; j < 12; j++) /* reset scroll position to top */
2873 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2875 /* get first visible line */
2876 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2877 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2879 /* get new current first visible line */
2880 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2882 ok(((r & 0xffffff00) == 0x00010000) &&
2883 ((r & 0x000000ff) != 0x00000000),
2884 "EM_SCROLL page down didn't scroll by a small positive number of "
2885 "lines (r == 0x%08x)\n", r);
2886 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2887 "(line %d scrolled to line %d\n", y_before, y_after);
2889 y_before = y_after;
2891 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2892 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2893 ok(((r & 0xffffff00) == 0x0001ff00),
2894 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2895 "(r == 0x%08x)\n", r);
2896 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2897 "%d scrolled to line %d\n", y_before, y_after);
2899 y_before = y_after;
2901 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2903 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2905 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2906 "(r == 0x%08x)\n", r);
2907 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2908 "1 line (%d scrolled to %d)\n", y_before, y_after);
2910 y_before = y_after;
2912 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2914 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2916 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2917 "(r == 0x%08x)\n", r);
2918 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2919 "line (%d scrolled to %d)\n", y_before, y_after);
2921 y_before = y_after;
2923 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2924 SB_LINEUP, 0); /* lineup beyond top */
2926 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2928 ok(r == 0x00010000,
2929 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2930 ok(y_before == y_after,
2931 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2933 y_before = y_after;
2935 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2936 SB_PAGEUP, 0);/*page up beyond top */
2938 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2940 ok(r == 0x00010000,
2941 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2942 ok(y_before == y_after,
2943 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2945 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2946 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2947 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2948 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2949 SB_PAGEDOWN, 0); /* page down beyond bot */
2950 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2952 ok(r == 0x00010000,
2953 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2954 ok(y_before == y_after,
2955 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2956 y_before, y_after);
2958 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2959 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2960 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2962 ok(r == 0x00010000,
2963 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2964 ok(y_before == y_after,
2965 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2966 y_before, y_after);
2968 DestroyWindow(hwndRichEdit);
2971 static unsigned int recursionLevel = 0;
2972 static unsigned int WM_SIZE_recursionLevel = 0;
2973 static BOOL bailedOutOfRecursion = FALSE;
2974 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2976 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2978 LRESULT r;
2980 if (bailedOutOfRecursion) return 0;
2981 if (recursionLevel >= 32) {
2982 bailedOutOfRecursion = TRUE;
2983 return 0;
2986 recursionLevel++;
2987 switch (message) {
2988 case WM_SIZE:
2989 WM_SIZE_recursionLevel++;
2990 r = richeditProc(hwnd, message, wParam, lParam);
2991 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2992 ShowScrollBar(hwnd, SB_VERT, TRUE);
2993 WM_SIZE_recursionLevel--;
2994 break;
2995 default:
2996 r = richeditProc(hwnd, message, wParam, lParam);
2997 break;
2999 recursionLevel--;
3000 return r;
3003 static void test_scrollbar_visibility(void)
3005 HWND hwndRichEdit;
3006 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
3007 SCROLLINFO si;
3008 WNDCLASSA cls;
3009 BOOL r;
3011 /* These tests show that richedit should temporarily refrain from automatically
3012 hiding or showing its scrollbars (vertical at least) when an explicit request
3013 is made via ShowScrollBar() or similar, outside of standard richedit logic.
3014 Some applications depend on forced showing (when otherwise richedit would
3015 hide the vertical scrollbar) and are thrown on an endless recursive loop
3016 if richedit auto-hides the scrollbar again. Apparently they never heard of
3017 the ES_DISABLENOSCROLL style... */
3019 hwndRichEdit = new_richedit(NULL);
3021 /* Test default scrollbar visibility behavior */
3022 memset(&si, 0, sizeof(si));
3023 si.cbSize = sizeof(si);
3024 si.fMask = SIF_PAGE | SIF_RANGE;
3025 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3026 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3027 "Vertical scrollbar is visible, should be invisible.\n");
3028 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3029 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3030 si.nPage, si.nMin, si.nMax);
3032 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3033 memset(&si, 0, sizeof(si));
3034 si.cbSize = sizeof(si);
3035 si.fMask = SIF_PAGE | SIF_RANGE;
3036 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3037 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3038 "Vertical scrollbar is visible, should be invisible.\n");
3039 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3040 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3041 si.nPage, si.nMin, si.nMax);
3043 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3044 memset(&si, 0, sizeof(si));
3045 si.cbSize = sizeof(si);
3046 si.fMask = SIF_PAGE | SIF_RANGE;
3047 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3048 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3049 "Vertical scrollbar is invisible, should be visible.\n");
3050 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3051 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3052 si.nPage, si.nMin, si.nMax);
3054 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
3055 even though it hides the scrollbar */
3056 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3057 memset(&si, 0, sizeof(si));
3058 si.cbSize = sizeof(si);
3059 si.fMask = SIF_PAGE | SIF_RANGE;
3060 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3061 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3062 "Vertical scrollbar is visible, should be invisible.\n");
3063 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3064 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3065 si.nPage, si.nMin, si.nMax);
3067 /* Setting non-scrolling text again does *not* reset scrollbar range */
3068 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3069 memset(&si, 0, sizeof(si));
3070 si.cbSize = sizeof(si);
3071 si.fMask = SIF_PAGE | SIF_RANGE;
3072 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3073 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3074 "Vertical scrollbar is visible, should be invisible.\n");
3075 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3076 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3077 si.nPage, si.nMin, si.nMax);
3079 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3080 memset(&si, 0, sizeof(si));
3081 si.cbSize = sizeof(si);
3082 si.fMask = SIF_PAGE | SIF_RANGE;
3083 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3084 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3085 "Vertical scrollbar is visible, should be invisible.\n");
3086 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3087 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3088 si.nPage, si.nMin, si.nMax);
3090 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3091 memset(&si, 0, sizeof(si));
3092 si.cbSize = sizeof(si);
3093 si.fMask = SIF_PAGE | SIF_RANGE;
3094 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3095 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3096 "Vertical scrollbar is visible, should be invisible.\n");
3097 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3098 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3099 si.nPage, si.nMin, si.nMax);
3101 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
3102 memset(&si, 0, sizeof(si));
3103 si.cbSize = sizeof(si);
3104 si.fMask = SIF_PAGE | SIF_RANGE;
3105 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3106 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3107 "Vertical scrollbar is visible, should be invisible.\n");
3108 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3109 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3110 si.nPage, si.nMin, si.nMax);
3112 DestroyWindow(hwndRichEdit);
3114 /* Test again, with ES_DISABLENOSCROLL style */
3115 hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
3117 /* Test default scrollbar visibility behavior */
3118 memset(&si, 0, sizeof(si));
3119 si.cbSize = sizeof(si);
3120 si.fMask = SIF_PAGE | SIF_RANGE;
3121 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3122 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3123 "Vertical scrollbar is invisible, should be visible.\n");
3124 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3125 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3126 si.nPage, si.nMin, si.nMax);
3128 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3129 memset(&si, 0, sizeof(si));
3130 si.cbSize = sizeof(si);
3131 si.fMask = SIF_PAGE | SIF_RANGE;
3132 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3133 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3134 "Vertical scrollbar is invisible, should be visible.\n");
3135 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
3136 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
3137 si.nPage, si.nMin, si.nMax);
3139 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3140 memset(&si, 0, sizeof(si));
3141 si.cbSize = sizeof(si);
3142 si.fMask = SIF_PAGE | SIF_RANGE;
3143 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3144 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3145 "Vertical scrollbar is invisible, should be visible.\n");
3146 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3147 "reported page/range is %d (%d..%d)\n",
3148 si.nPage, si.nMin, si.nMax);
3150 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
3151 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 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 > 1,
3159 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3160 si.nPage, si.nMin, si.nMax);
3162 /* Setting non-scrolling text again does *not* reset scrollbar range */
3163 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3164 memset(&si, 0, sizeof(si));
3165 si.cbSize = sizeof(si);
3166 si.fMask = SIF_PAGE | SIF_RANGE;
3167 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3168 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3169 "Vertical scrollbar is invisible, should be visible.\n");
3170 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3171 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3172 si.nPage, si.nMin, si.nMax);
3174 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 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 > 1,
3182 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3183 si.nPage, si.nMin, si.nMax);
3185 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3186 memset(&si, 0, sizeof(si));
3187 si.cbSize = sizeof(si);
3188 si.fMask = SIF_PAGE | SIF_RANGE;
3189 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3190 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3191 "Vertical scrollbar is invisible, should be visible.\n");
3192 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3193 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3194 si.nPage, si.nMin, si.nMax);
3196 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
3197 memset(&si, 0, sizeof(si));
3198 si.cbSize = sizeof(si);
3199 si.fMask = SIF_PAGE | SIF_RANGE;
3200 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3201 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3202 "Vertical scrollbar is invisible, should be visible.\n");
3203 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
3204 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3205 si.nPage, si.nMin, si.nMax);
3207 DestroyWindow(hwndRichEdit);
3209 /* Test behavior with explicit visibility request, using ShowScrollBar() */
3210 hwndRichEdit = new_richedit(NULL);
3212 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3213 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
3214 memset(&si, 0, sizeof(si));
3215 si.cbSize = sizeof(si);
3216 si.fMask = SIF_PAGE | SIF_RANGE;
3217 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3218 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3219 "Vertical scrollbar is invisible, should be visible.\n");
3220 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3221 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3222 si.nPage, si.nMin, si.nMax);
3224 /* Ditto, see above */
3225 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3226 memset(&si, 0, sizeof(si));
3227 si.cbSize = sizeof(si);
3228 si.fMask = SIF_PAGE | SIF_RANGE;
3229 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3230 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3231 "Vertical scrollbar is invisible, should be visible.\n");
3232 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3233 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3234 si.nPage, si.nMin, si.nMax);
3236 /* Ditto, see above */
3237 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3238 memset(&si, 0, sizeof(si));
3239 si.cbSize = sizeof(si);
3240 si.fMask = SIF_PAGE | SIF_RANGE;
3241 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3242 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3243 "Vertical scrollbar is invisible, should be visible.\n");
3244 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3245 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3246 si.nPage, si.nMin, si.nMax);
3248 /* Ditto, see above */
3249 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3250 memset(&si, 0, sizeof(si));
3251 si.cbSize = sizeof(si);
3252 si.fMask = SIF_PAGE | SIF_RANGE;
3253 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3254 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3255 "Vertical scrollbar is invisible, should be visible.\n");
3256 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3257 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3258 si.nPage, si.nMin, si.nMax);
3260 /* Ditto, see above */
3261 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3262 memset(&si, 0, sizeof(si));
3263 si.cbSize = sizeof(si);
3264 si.fMask = SIF_PAGE | SIF_RANGE;
3265 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3266 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3267 "Vertical scrollbar is invisible, should be visible.\n");
3268 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
3269 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
3270 si.nPage, si.nMin, si.nMax);
3272 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3273 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3274 memset(&si, 0, sizeof(si));
3275 si.cbSize = sizeof(si);
3276 si.fMask = SIF_PAGE | SIF_RANGE;
3277 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3278 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3279 "Vertical scrollbar is visible, should be invisible.\n");
3280 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3281 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3282 si.nPage, si.nMin, si.nMax);
3284 DestroyWindow(hwndRichEdit);
3286 hwndRichEdit = new_richedit(NULL);
3288 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3289 memset(&si, 0, sizeof(si));
3290 si.cbSize = sizeof(si);
3291 si.fMask = SIF_PAGE | SIF_RANGE;
3292 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3293 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3294 "Vertical scrollbar is visible, should be invisible.\n");
3295 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3296 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3297 si.nPage, si.nMin, si.nMax);
3299 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3300 memset(&si, 0, sizeof(si));
3301 si.cbSize = sizeof(si);
3302 si.fMask = SIF_PAGE | SIF_RANGE;
3303 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3304 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3305 "Vertical scrollbar is visible, should be invisible.\n");
3306 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3307 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3308 si.nPage, si.nMin, si.nMax);
3310 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3311 memset(&si, 0, sizeof(si));
3312 si.cbSize = sizeof(si);
3313 si.fMask = SIF_PAGE | SIF_RANGE;
3314 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3315 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3316 "Vertical scrollbar is visible, should be invisible.\n");
3317 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3318 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3319 si.nPage, si.nMin, si.nMax);
3321 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3322 memset(&si, 0, sizeof(si));
3323 si.cbSize = sizeof(si);
3324 si.fMask = SIF_PAGE | SIF_RANGE;
3325 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3326 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3327 "Vertical scrollbar is visible, should be invisible.\n");
3328 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3329 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3330 si.nPage, si.nMin, si.nMax);
3332 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3333 memset(&si, 0, sizeof(si));
3334 si.cbSize = sizeof(si);
3335 si.fMask = SIF_PAGE | SIF_RANGE;
3336 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3337 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3338 "Vertical scrollbar is invisible, should be visible.\n");
3339 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3340 "reported page/range is %d (%d..%d)\n",
3341 si.nPage, si.nMin, si.nMax);
3343 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3344 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3345 memset(&si, 0, sizeof(si));
3346 si.cbSize = sizeof(si);
3347 si.fMask = SIF_PAGE | SIF_RANGE;
3348 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3349 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3350 "Vertical scrollbar is visible, should be invisible.\n");
3351 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3352 "reported page/range is %d (%d..%d)\n",
3353 si.nPage, si.nMin, si.nMax);
3355 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3356 memset(&si, 0, sizeof(si));
3357 si.cbSize = sizeof(si);
3358 si.fMask = SIF_PAGE | SIF_RANGE;
3359 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3360 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3361 "Vertical scrollbar is visible, should be invisible.\n");
3362 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3363 "reported page/range is %d (%d..%d)\n",
3364 si.nPage, si.nMin, si.nMax);
3366 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3367 EM_SCROLL will make visible any forcefully invisible scrollbar */
3368 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3369 memset(&si, 0, sizeof(si));
3370 si.cbSize = sizeof(si);
3371 si.fMask = SIF_PAGE | SIF_RANGE;
3372 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3373 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3374 "Vertical scrollbar is invisible, should be visible.\n");
3375 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3376 "reported page/range is %d (%d..%d)\n",
3377 si.nPage, si.nMin, si.nMax);
3379 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3380 memset(&si, 0, sizeof(si));
3381 si.cbSize = sizeof(si);
3382 si.fMask = SIF_PAGE | SIF_RANGE;
3383 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3384 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3385 "Vertical scrollbar is visible, should be invisible.\n");
3386 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3387 "reported page/range is %d (%d..%d)\n",
3388 si.nPage, si.nMin, si.nMax);
3390 /* Again, EM_SCROLL, with SB_LINEUP */
3391 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3392 memset(&si, 0, sizeof(si));
3393 si.cbSize = sizeof(si);
3394 si.fMask = SIF_PAGE | SIF_RANGE;
3395 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3396 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3397 "Vertical scrollbar is invisible, should be visible.\n");
3398 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3399 "reported page/range is %d (%d..%d)\n",
3400 si.nPage, si.nMin, si.nMax);
3402 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3403 memset(&si, 0, sizeof(si));
3404 si.cbSize = sizeof(si);
3405 si.fMask = SIF_PAGE | SIF_RANGE;
3406 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3407 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3408 "Vertical scrollbar is visible, should be invisible.\n");
3409 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3410 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3411 si.nPage, si.nMin, si.nMax);
3413 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3414 memset(&si, 0, sizeof(si));
3415 si.cbSize = sizeof(si);
3416 si.fMask = SIF_PAGE | SIF_RANGE;
3417 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3418 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3419 "Vertical scrollbar is invisible, should be visible.\n");
3420 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3421 "reported page/range is %d (%d..%d)\n",
3422 si.nPage, si.nMin, si.nMax);
3424 DestroyWindow(hwndRichEdit);
3427 /* Test behavior with explicit visibility request, using SetWindowLongA()() */
3428 hwndRichEdit = new_richedit(NULL);
3430 #define ENABLE_WS_VSCROLL(hwnd) \
3431 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
3432 #define DISABLE_WS_VSCROLL(hwnd) \
3433 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
3435 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3436 ENABLE_WS_VSCROLL(hwndRichEdit);
3437 memset(&si, 0, sizeof(si));
3438 si.cbSize = sizeof(si);
3439 si.fMask = SIF_PAGE | SIF_RANGE;
3440 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3441 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3442 "Vertical scrollbar is invisible, should be visible.\n");
3443 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3444 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3445 si.nPage, si.nMin, si.nMax);
3447 /* Ditto, see above */
3448 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3449 memset(&si, 0, sizeof(si));
3450 si.cbSize = sizeof(si);
3451 si.fMask = SIF_PAGE | SIF_RANGE;
3452 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3453 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3454 "Vertical scrollbar is invisible, should be visible.\n");
3455 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3456 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3457 si.nPage, si.nMin, si.nMax);
3459 /* Ditto, see above */
3460 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3461 memset(&si, 0, sizeof(si));
3462 si.cbSize = sizeof(si);
3463 si.fMask = SIF_PAGE | SIF_RANGE;
3464 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3465 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3466 "Vertical scrollbar is invisible, should be visible.\n");
3467 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3468 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3469 si.nPage, si.nMin, si.nMax);
3471 /* Ditto, see above */
3472 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3473 memset(&si, 0, sizeof(si));
3474 si.cbSize = sizeof(si);
3475 si.fMask = SIF_PAGE | SIF_RANGE;
3476 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3477 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3478 "Vertical scrollbar is invisible, should be visible.\n");
3479 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3480 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3481 si.nPage, si.nMin, si.nMax);
3483 /* Ditto, see above */
3484 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3485 memset(&si, 0, sizeof(si));
3486 si.cbSize = sizeof(si);
3487 si.fMask = SIF_PAGE | SIF_RANGE;
3488 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3489 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3490 "Vertical scrollbar is invisible, should be visible.\n");
3491 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3492 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3493 si.nPage, si.nMin, si.nMax);
3495 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3496 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3497 memset(&si, 0, sizeof(si));
3498 si.cbSize = sizeof(si);
3499 si.fMask = SIF_PAGE | SIF_RANGE;
3500 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3501 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3502 "Vertical scrollbar is visible, should be invisible.\n");
3503 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3504 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3505 si.nPage, si.nMin, si.nMax);
3507 DestroyWindow(hwndRichEdit);
3509 hwndRichEdit = new_richedit(NULL);
3511 DISABLE_WS_VSCROLL(hwndRichEdit);
3512 memset(&si, 0, sizeof(si));
3513 si.cbSize = sizeof(si);
3514 si.fMask = SIF_PAGE | SIF_RANGE;
3515 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3516 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3517 "Vertical scrollbar is visible, should be invisible.\n");
3518 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3519 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3520 si.nPage, si.nMin, si.nMax);
3522 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3523 memset(&si, 0, sizeof(si));
3524 si.cbSize = sizeof(si);
3525 si.fMask = SIF_PAGE | SIF_RANGE;
3526 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3527 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3528 "Vertical scrollbar is visible, should be invisible.\n");
3529 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3530 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3531 si.nPage, si.nMin, si.nMax);
3533 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3534 memset(&si, 0, sizeof(si));
3535 si.cbSize = sizeof(si);
3536 si.fMask = SIF_PAGE | SIF_RANGE;
3537 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3538 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3539 "Vertical scrollbar is visible, should be invisible.\n");
3540 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3541 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3542 si.nPage, si.nMin, si.nMax);
3544 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3545 memset(&si, 0, sizeof(si));
3546 si.cbSize = sizeof(si);
3547 si.fMask = SIF_PAGE | SIF_RANGE;
3548 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3549 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3550 "Vertical scrollbar is visible, should be invisible.\n");
3551 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3552 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3553 si.nPage, si.nMin, si.nMax);
3555 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3556 memset(&si, 0, sizeof(si));
3557 si.cbSize = sizeof(si);
3558 si.fMask = SIF_PAGE | SIF_RANGE;
3559 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3560 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3561 "Vertical scrollbar is invisible, should be visible.\n");
3562 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3563 "reported page/range is %d (%d..%d)\n",
3564 si.nPage, si.nMin, si.nMax);
3566 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3567 DISABLE_WS_VSCROLL(hwndRichEdit);
3568 memset(&si, 0, sizeof(si));
3569 si.cbSize = sizeof(si);
3570 si.fMask = SIF_PAGE | SIF_RANGE;
3571 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3572 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3573 "Vertical scrollbar is visible, should be invisible.\n");
3574 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3575 "reported page/range is %d (%d..%d)\n",
3576 si.nPage, si.nMin, si.nMax);
3578 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3579 memset(&si, 0, sizeof(si));
3580 si.cbSize = sizeof(si);
3581 si.fMask = SIF_PAGE | SIF_RANGE;
3582 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3583 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3584 "Vertical scrollbar is visible, should be invisible.\n");
3585 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3586 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3587 si.nPage, si.nMin, si.nMax);
3589 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3590 memset(&si, 0, sizeof(si));
3591 si.cbSize = sizeof(si);
3592 si.fMask = SIF_PAGE | SIF_RANGE;
3593 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3594 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3595 "Vertical scrollbar is invisible, should be visible.\n");
3596 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3597 "reported page/range is %d (%d..%d)\n",
3598 si.nPage, si.nMin, si.nMax);
3600 DISABLE_WS_VSCROLL(hwndRichEdit);
3601 memset(&si, 0, sizeof(si));
3602 si.cbSize = sizeof(si);
3603 si.fMask = SIF_PAGE | SIF_RANGE;
3604 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3605 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3606 "Vertical scrollbar is visible, should be invisible.\n");
3607 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3608 "reported page/range is %d (%d..%d)\n",
3609 si.nPage, si.nMin, si.nMax);
3611 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3612 EM_SCROLL will make visible any forcefully invisible scrollbar */
3613 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3614 memset(&si, 0, sizeof(si));
3615 si.cbSize = sizeof(si);
3616 si.fMask = SIF_PAGE | SIF_RANGE;
3617 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3618 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3619 "Vertical scrollbar is invisible, should be visible.\n");
3620 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3621 "reported page/range is %d (%d..%d)\n",
3622 si.nPage, si.nMin, si.nMax);
3624 DISABLE_WS_VSCROLL(hwndRichEdit);
3625 memset(&si, 0, sizeof(si));
3626 si.cbSize = sizeof(si);
3627 si.fMask = SIF_PAGE | SIF_RANGE;
3628 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3629 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3630 "Vertical scrollbar is visible, should be invisible.\n");
3631 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3632 "reported page/range is %d (%d..%d)\n",
3633 si.nPage, si.nMin, si.nMax);
3635 /* Again, EM_SCROLL, with SB_LINEUP */
3636 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3637 memset(&si, 0, sizeof(si));
3638 si.cbSize = sizeof(si);
3639 si.fMask = SIF_PAGE | SIF_RANGE;
3640 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3641 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3642 "Vertical scrollbar is invisible, should be visible.\n");
3643 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3644 "reported page/range is %d (%d..%d)\n",
3645 si.nPage, si.nMin, si.nMax);
3647 DestroyWindow(hwndRichEdit);
3649 /* This window proc models what is going on with Corman Lisp 3.0.
3650 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3651 force the scrollbar into visibility. Recursion should NOT happen
3652 as a result of this action.
3654 r = GetClassInfoA(NULL, RICHEDIT_CLASS20A, &cls);
3655 if (r) {
3656 richeditProc = cls.lpfnWndProc;
3657 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3658 cls.lpszClassName = "RicheditStupidOverride";
3659 if(!RegisterClassA(&cls)) assert(0);
3661 recursionLevel = 0;
3662 WM_SIZE_recursionLevel = 0;
3663 bailedOutOfRecursion = FALSE;
3664 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3665 ok(!bailedOutOfRecursion,
3666 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3668 recursionLevel = 0;
3669 WM_SIZE_recursionLevel = 0;
3670 bailedOutOfRecursion = FALSE;
3671 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3672 ok(!bailedOutOfRecursion,
3673 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3675 /* Unblock window in order to process WM_DESTROY */
3676 recursionLevel = 0;
3677 bailedOutOfRecursion = FALSE;
3678 WM_SIZE_recursionLevel = 0;
3679 DestroyWindow(hwndRichEdit);
3683 static void test_EM_SETUNDOLIMIT(void)
3685 /* cases we test for:
3686 * default behaviour - limiting at 100 undo's
3687 * undo disabled - setting a limit of 0
3688 * undo limited - undo limit set to some to some number, like 2
3689 * bad input - sending a negative number should default to 100 undo's */
3691 HWND hwndRichEdit = new_richedit(NULL);
3692 CHARRANGE cr;
3693 int i;
3694 int result;
3696 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3697 cr.cpMin = 0;
3698 cr.cpMax = -1;
3699 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3701 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3702 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3703 also, multiple pastes don't combine like WM_CHAR would */
3705 /* first case - check the default */
3706 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3707 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3708 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3709 for (i=0; i<100; i++) /* Undo 100 of them */
3710 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3711 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3712 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3714 /* second case - cannot undo */
3715 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3716 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3717 SendMessageA(hwndRichEdit,
3718 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3719 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3720 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3722 /* third case - set it to an arbitrary number */
3723 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3724 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3725 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3726 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3727 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3728 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3729 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3730 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3731 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3732 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3733 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3734 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3735 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3736 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3738 /* fourth case - setting negative numbers should default to 100 undos */
3739 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3740 result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3741 ok (result == 100,
3742 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3744 DestroyWindow(hwndRichEdit);
3747 static void test_ES_PASSWORD(void)
3749 /* This isn't hugely testable, so we're just going to run it through its paces */
3751 HWND hwndRichEdit = new_richedit(NULL);
3752 WCHAR result;
3754 /* First, check the default of a regular control */
3755 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3756 ok (result == 0,
3757 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3759 /* Now, set it to something normal */
3760 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3761 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3762 ok (result == 120,
3763 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3765 /* Now, set it to something odd */
3766 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3767 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3768 ok (result == 1234,
3769 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3770 DestroyWindow(hwndRichEdit);
3773 LONG streamout_written = 0;
3775 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3776 LPBYTE pbBuff,
3777 LONG cb,
3778 LONG *pcb)
3780 char** str = (char**)dwCookie;
3781 *pcb = cb;
3782 if (*pcb > 0) {
3783 memcpy(*str, pbBuff, *pcb);
3784 *str += *pcb;
3786 streamout_written = *pcb;
3787 return 0;
3790 static void test_WM_SETTEXT(void)
3792 HWND hwndRichEdit = new_richedit(NULL);
3793 const char * TestItem1 = "TestSomeText";
3794 const char * TestItem2 = "TestSomeText\r";
3795 const char * TestItem2_after = "TestSomeText\r\n";
3796 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3797 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3798 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3799 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3800 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3801 const char * TestItem5_after = "TestSomeText TestSomeText";
3802 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3803 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3804 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3805 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3807 const char rtftextA[] = "{\\rtf sometext}";
3808 const char urtftextA[] = "{\\urtf sometext}";
3809 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3810 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3811 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3813 char buf[1024] = {0};
3814 WCHAR bufW[1024] = {0};
3815 LRESULT result;
3817 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3818 any solitary \r to be converted to \r\n on return. Properly paired
3819 \r\n are not affected. It also shows that the special sequence \r\r\n
3820 gets converted to a single space.
3823 #define TEST_SETTEXT(a, b) \
3824 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3825 ok (result == 1, "WM_SETTEXT returned %Id instead of 1\n", result); \
3826 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3827 ok (result == lstrlenA(buf), \
3828 "WM_GETTEXT returned %Id instead of expected %u\n", \
3829 result, lstrlenA(buf)); \
3830 result = strcmp(b, buf); \
3831 ok(result == 0, \
3832 "WM_SETTEXT round trip: strcmp = %Id, text=\"%s\"\n", result, buf);
3834 TEST_SETTEXT(TestItem1, TestItem1)
3835 TEST_SETTEXT(TestItem2, TestItem2_after)
3836 TEST_SETTEXT(TestItem3, TestItem3_after)
3837 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3838 TEST_SETTEXT(TestItem4, TestItem4_after)
3839 TEST_SETTEXT(TestItem5, TestItem5_after)
3840 TEST_SETTEXT(TestItem6, TestItem6_after)
3841 TEST_SETTEXT(TestItem7, TestItem7_after)
3843 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3844 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3845 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3846 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3847 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3848 DestroyWindow(hwndRichEdit);
3849 #undef TEST_SETTEXT
3851 #define TEST_SETTEXTW(a, b) \
3852 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3853 ok (result == 1, "WM_SETTEXT returned %Id instead of 1\n", result); \
3854 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3855 ok (result == lstrlenW(bufW), \
3856 "WM_GETTEXT returned %Id instead of expected %u\n", \
3857 result, lstrlenW(bufW)); \
3858 result = lstrcmpW(b, bufW); \
3859 ok(result == 0, "WM_SETTEXT round trip: strcmp = %Id\n", result);
3861 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3862 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3863 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3864 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3865 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3866 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3867 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3868 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3869 DestroyWindow(hwndRichEdit);
3870 #undef TEST_SETTEXTW
3872 /* Single-line richedit */
3873 hwndRichEdit = new_richedit_with_style(NULL, 0);
3874 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"line1\r\nline2");
3875 ok(result == 1, "WM_SETTEXT returned %Id, expected 12\n", result);
3876 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3877 ok(result == 5, "WM_GETTEXT returned %Id, expected 5\n", result);
3878 ok(!strcmp(buf, "line1"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3879 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}");
3880 ok(result == 1, "WM_SETTEXT returned %Id, expected 1\n", result);
3881 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
3882 ok(result == 3, "WM_GETTEXT returned %Id, expected 3\n", result);
3883 ok(!strcmp(buf, "ABC"), "WM_GETTEXT returned incorrect string '%s'\n", buf);
3884 DestroyWindow(hwndRichEdit);
3887 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3888 resent to the callkack. */
3889 static DWORD CALLBACK test_esCallback_written_1(DWORD_PTR dwCookie,
3890 LPBYTE pbBuff,
3891 LONG cb,
3892 LONG *pcb)
3894 char** str = (char**)dwCookie;
3895 ok(*pcb == cb || *pcb == 0, "cb %ld, *pcb %ld\n", cb, *pcb);
3896 *pcb = 0;
3897 if (cb > 0) {
3898 memcpy(*str, pbBuff, cb);
3899 *str += cb;
3900 *pcb = 1;
3902 return 0;
3905 static int count_pars(const char *buf)
3907 const char *p = buf;
3908 int count = 0;
3909 while ((p = strstr( p, "\\par" )) != NULL)
3911 if (!isalpha( p[4] ))
3912 count++;
3913 p++;
3915 return count;
3918 static void test_EM_STREAMOUT(void)
3920 HWND hwndRichEdit = new_richedit(NULL);
3921 int r;
3922 EDITSTREAM es;
3923 char buf[1024] = {0};
3924 char * p;
3925 LRESULT result;
3927 const char * TestItem1 = "TestSomeText";
3928 const char * TestItem2 = "TestSomeText\r";
3929 const char * TestItem3 = "TestSomeText\r\n";
3931 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3932 p = buf;
3933 es.dwCookie = (DWORD_PTR)&p;
3934 es.dwError = 0;
3935 es.pfnCallback = test_WM_SETTEXT_esCallback;
3936 memset(buf, 0, sizeof(buf));
3937 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3938 r = strlen(buf);
3939 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3940 ok(strcmp(buf, TestItem1) == 0,
3941 "streamed text different, got %s\n", buf);
3942 ok(result == streamout_written, "got %Id expected %ld\n", result, streamout_written);
3944 /* RTF mode writes the final end of para \r if it's part of the selection */
3945 p = buf;
3946 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3947 ok (count_pars(buf) == 1, "got %s\n", buf);
3948 ok(result == streamout_written, "got %Id expected %ld\n", result, streamout_written);
3949 p = buf;
3950 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 12);
3951 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3952 ok (count_pars(buf) == 0, "got %s\n", buf);
3953 ok(result == streamout_written, "got %Id expected %ld\n", result, streamout_written);
3954 p = buf;
3955 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3956 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3957 ok (count_pars(buf) == 1, "got %s\n", buf);
3958 ok(result == streamout_written, "got %Id expected %ld\n", result, streamout_written);
3960 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3961 p = buf;
3962 es.dwCookie = (DWORD_PTR)&p;
3963 es.dwError = 0;
3964 es.pfnCallback = test_WM_SETTEXT_esCallback;
3965 memset(buf, 0, sizeof(buf));
3966 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3967 ok(result == streamout_written, "got %Id expected %ld\n", result, streamout_written);
3968 r = strlen(buf);
3969 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3970 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3971 ok(strcmp(buf, TestItem3) == 0,
3972 "streamed text different from, got %s\n", buf);
3974 /* And again RTF mode writes the final end of para \r if it's part of the selection */
3975 p = buf;
3976 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3977 ok (count_pars(buf) == 2, "got %s\n", buf);
3978 ok(result == streamout_written, "got %Id expected %ld\n", result, streamout_written);
3979 p = buf;
3980 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 13);
3981 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3982 ok (count_pars(buf) == 1, "got %s\n", buf);
3983 ok(result == streamout_written, "got %Id expected %ld\n", result, streamout_written);
3984 p = buf;
3985 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3986 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3987 ok (count_pars(buf) == 2, "got %s\n", buf);
3988 ok(result == streamout_written, "got %Id expected %ld\n", result, streamout_written);
3990 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3991 p = buf;
3992 es.dwCookie = (DWORD_PTR)&p;
3993 es.dwError = 0;
3994 es.pfnCallback = test_WM_SETTEXT_esCallback;
3995 memset(buf, 0, sizeof(buf));
3996 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3997 ok(result == streamout_written, "got %Id expected %ld\n", result, streamout_written);
3998 r = strlen(buf);
3999 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
4000 ok(strcmp(buf, TestItem3) == 0,
4001 "streamed text different, got %s\n", buf);
4003 /* Use a callback that sets *pcb to one */
4004 p = buf;
4005 es.dwCookie = (DWORD_PTR)&p;
4006 es.dwError = 0;
4007 es.pfnCallback = test_esCallback_written_1;
4008 memset(buf, 0, sizeof(buf));
4009 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
4010 r = strlen(buf);
4011 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
4012 ok(strcmp(buf, TestItem3) == 0,
4013 "streamed text different, got %s\n", buf);
4014 ok(result == 0, "got %Id expected 0\n", result);
4017 DestroyWindow(hwndRichEdit);
4020 static void test_EM_STREAMOUT_FONTTBL(void)
4022 HWND hwndRichEdit = new_richedit(NULL);
4023 EDITSTREAM es;
4024 char buf[1024] = {0};
4025 char * p;
4026 char * fontTbl;
4027 int brackCount;
4029 const char * TestItem = "TestSomeText";
4031 /* fills in the richedit control with some text */
4032 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
4034 /* streams out the text in rtf format */
4035 p = buf;
4036 es.dwCookie = (DWORD_PTR)&p;
4037 es.dwError = 0;
4038 es.pfnCallback = test_WM_SETTEXT_esCallback;
4039 memset(buf, 0, sizeof(buf));
4040 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
4042 /* scans for \fonttbl, error if not found */
4043 fontTbl = strstr(buf, "\\fonttbl");
4044 ok(fontTbl != NULL, "missing \\fonttbl section\n");
4045 if(fontTbl)
4047 /* scans for terminating closing bracket */
4048 brackCount = 1;
4049 while(*fontTbl && brackCount)
4051 if(*fontTbl == '{')
4052 brackCount++;
4053 else if(*fontTbl == '}')
4054 brackCount--;
4055 fontTbl++;
4057 /* checks whether closing bracket is ok */
4058 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
4059 if(!brackCount)
4061 /* char before closing fonttbl block should be a closed bracket */
4062 fontTbl -= 2;
4063 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
4065 /* char after fonttbl block should be a crlf */
4066 fontTbl += 2;
4067 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
4070 DestroyWindow(hwndRichEdit);
4073 static void test_EM_STREAMOUT_empty_para(void)
4075 HWND hwnd = new_richedit(NULL);
4076 char buf[1024], *p = buf;
4077 EDITSTREAM es;
4079 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
4081 memset(buf, 0, sizeof(buf));
4082 es.dwCookie = (DWORD_PTR)&p;
4083 es.dwError = 0;
4084 es.pfnCallback = test_WM_SETTEXT_esCallback;
4086 SendMessageA(hwnd, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
4087 ok((p = strstr(buf, "\\pard")) != NULL, "missing \\pard\n");
4088 ok(((p = strstr(p, "\\fs")) && isdigit(p[3])), "missing \\fs\n");
4090 DestroyWindow(hwnd);
4093 static void test_EM_SETTEXTEX(void)
4095 HWND hwndRichEdit, parent;
4096 SCROLLINFO si;
4097 int sel_start, sel_end;
4098 SETTEXTEX setText;
4099 GETTEXTEX getText;
4100 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4101 'S', 'o', 'm', 'e',
4102 'T', 'e', 'x', 't', 0};
4103 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
4104 't', 'S', 'o', 'm',
4105 'e', 'T', 'e', 'x',
4106 't', 't', 'S', 'o',
4107 'm', 'e', 'T', 'e',
4108 'x', 't', 0};
4109 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
4110 '\r','t','S','o','m','e','T','e','x','t',0};
4111 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4112 'S', 'o', 'm', 'e',
4113 'T', 'e', 'x', 't',
4114 '\r', 0};
4115 const char * TestItem2_after = "TestSomeText\r\n";
4116 WCHAR TestItem3[] = {'T', 'e', 's', 't',
4117 'S', 'o', 'm', 'e',
4118 'T', 'e', 'x', 't',
4119 '\r','\n','\r','\n', 0};
4120 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
4121 'S', 'o', 'm', 'e',
4122 'T', 'e', 'x', 't',
4123 '\n','\n', 0};
4124 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
4125 'S', 'o', 'm', 'e',
4126 'T', 'e', 'x', 't',
4127 '\r','\r', 0};
4128 WCHAR TestItem4[] = {'T', 'e', 's', 't',
4129 'S', 'o', 'm', 'e',
4130 'T', 'e', 'x', 't',
4131 '\r','\r','\n','\r',
4132 '\n', 0};
4133 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
4134 'S', 'o', 'm', 'e',
4135 'T', 'e', 'x', 't',
4136 ' ','\r', 0};
4137 #define MAX_BUF_LEN 1024
4138 WCHAR buf[MAX_BUF_LEN];
4139 char bufACP[MAX_BUF_LEN];
4140 char * p;
4141 int result;
4142 CHARRANGE cr;
4143 EDITSTREAM es;
4144 WNDCLASSA cls;
4146 /* Test the scroll position with and without a parent window.
4148 * For some reason the scroll position is 0 after EM_SETTEXTEX
4149 * with the ST_SELECTION flag only when the control has a parent
4150 * window, even though the selection is at the end. */
4151 cls = make_simple_class(DefWindowProcA, "ParentTestClass");
4152 if(!RegisterClassA(&cls)) assert(0);
4154 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4155 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4156 ok (parent != 0, "Failed to create parent window\n");
4158 hwndRichEdit = CreateWindowExA(0,
4159 RICHEDIT_CLASS20A, NULL,
4160 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
4161 0, 0, 200, 60, parent, NULL,
4162 hmoduleRichEdit, NULL);
4164 setText.codepage = CP_ACP;
4165 setText.flags = ST_SELECTION;
4166 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4167 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4168 todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4169 si.cbSize = sizeof(si);
4170 si.fMask = SIF_ALL;
4171 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4172 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4173 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4174 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4175 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4177 DestroyWindow(parent);
4179 /* Test without a parent window */
4180 hwndRichEdit = new_richedit(NULL);
4181 setText.codepage = CP_ACP;
4182 setText.flags = ST_SELECTION;
4183 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4184 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4185 todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
4186 si.cbSize = sizeof(si);
4187 si.fMask = SIF_ALL;
4188 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4189 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
4190 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4191 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
4192 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
4194 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
4195 * but this time it is because the selection is at the beginning. */
4196 setText.codepage = CP_ACP;
4197 setText.flags = ST_DEFAULT;
4198 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
4199 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
4200 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4201 si.cbSize = sizeof(si);
4202 si.fMask = SIF_ALL;
4203 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
4204 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
4205 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4206 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
4207 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
4209 setText.codepage = 1200; /* no constant for unicode */
4210 getText.codepage = 1200; /* no constant for unicode */
4211 getText.cb = MAX_BUF_LEN;
4212 getText.flags = GT_DEFAULT;
4213 getText.lpDefaultChar = NULL;
4214 getText.lpUsedDefChar = NULL;
4216 setText.flags = 0;
4217 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4218 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4219 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4220 ok(lstrcmpW(buf, TestItem1) == 0,
4221 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4223 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
4224 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
4226 setText.codepage = 1200; /* no constant for unicode */
4227 getText.codepage = 1200; /* no constant for unicode */
4228 getText.cb = MAX_BUF_LEN;
4229 getText.flags = GT_DEFAULT;
4230 getText.lpDefaultChar = NULL;
4231 getText.lpUsedDefChar = NULL;
4232 setText.flags = 0;
4233 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
4234 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4235 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4236 ok(lstrcmpW(buf, TestItem2) == 0,
4237 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4239 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
4240 SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
4241 ok(strcmp((const char *)buf, TestItem2_after) == 0,
4242 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
4244 /* Baseline test for just-enough buffer space for string */
4245 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4246 getText.codepage = 1200; /* no constant for unicode */
4247 getText.flags = GT_DEFAULT;
4248 getText.lpDefaultChar = NULL;
4249 getText.lpUsedDefChar = NULL;
4250 memset(buf, 0, sizeof(buf));
4251 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4252 ok(lstrcmpW(buf, TestItem2) == 0,
4253 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4255 /* When there is enough space for one character, but not both, of the CRLF
4256 pair at the end of the string, the CR is not copied at all. That is,
4257 the caller must not see CRLF pairs truncated to CR at the end of the
4258 string.
4260 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
4261 getText.codepage = 1200; /* no constant for unicode */
4262 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
4263 getText.lpDefaultChar = NULL;
4264 getText.lpUsedDefChar = NULL;
4265 memset(buf, 0, sizeof(buf));
4266 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4267 ok(lstrcmpW(buf, TestItem1) == 0,
4268 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4271 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
4272 setText.codepage = 1200; /* no constant for unicode */
4273 getText.codepage = 1200; /* no constant for unicode */
4274 getText.cb = MAX_BUF_LEN;
4275 getText.flags = GT_DEFAULT;
4276 getText.lpDefaultChar = NULL;
4277 getText.lpUsedDefChar = NULL;
4278 setText.flags = 0;
4279 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
4280 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4281 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4282 ok(lstrcmpW(buf, TestItem3_after) == 0,
4283 "EM_SETTEXTEX did not convert properly\n");
4285 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
4286 setText.codepage = 1200; /* no constant for unicode */
4287 getText.codepage = 1200; /* no constant for unicode */
4288 getText.cb = MAX_BUF_LEN;
4289 getText.flags = GT_DEFAULT;
4290 getText.lpDefaultChar = NULL;
4291 getText.lpUsedDefChar = NULL;
4292 setText.flags = 0;
4293 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
4294 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4295 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4296 ok(lstrcmpW(buf, TestItem3_after) == 0,
4297 "EM_SETTEXTEX did not convert properly\n");
4299 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
4300 setText.codepage = 1200; /* no constant for unicode */
4301 getText.codepage = 1200; /* no constant for unicode */
4302 getText.cb = MAX_BUF_LEN;
4303 getText.flags = GT_DEFAULT;
4304 getText.lpDefaultChar = NULL;
4305 getText.lpUsedDefChar = NULL;
4306 setText.flags = 0;
4307 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
4308 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4309 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4310 ok(lstrcmpW(buf, TestItem4_after) == 0,
4311 "EM_SETTEXTEX did not convert properly\n");
4313 /* !ST_SELECTION && Unicode && !\rtf */
4314 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4315 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4317 ok (result == 1,
4318 "EM_SETTEXTEX returned %d, instead of 1\n",result);
4319 ok(!buf[0], "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
4321 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4322 setText.flags = 0;
4323 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4324 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4325 /* select some text */
4326 cr.cpMax = 1;
4327 cr.cpMin = 3;
4328 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4329 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4330 setText.flags = ST_SELECTION;
4331 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
4332 ok(result == 0,
4333 "EM_SETTEXTEX with NULL lParam to replace selection"
4334 " with no text should return 0. Got %i\n",
4335 result);
4337 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
4338 setText.flags = 0;
4339 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4340 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4341 /* select some text */
4342 cr.cpMax = 1;
4343 cr.cpMin = 3;
4344 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4345 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
4346 setText.flags = ST_SELECTION;
4347 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4348 /* get text */
4349 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4350 ok(result == lstrlenW(TestItem1),
4351 "EM_SETTEXTEX with NULL lParam to replace selection"
4352 " with no text should return 0. Got %i\n",
4353 result);
4354 ok(lstrlenW(buf) == 22,
4355 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
4356 lstrlenW(buf) );
4358 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
4359 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4360 p = (char *)buf;
4361 es.dwCookie = (DWORD_PTR)&p;
4362 es.dwError = 0;
4363 es.pfnCallback = test_WM_SETTEXT_esCallback;
4364 memset(buf, 0, sizeof(buf));
4365 SendMessageA(hwndRichEdit, EM_STREAMOUT,
4366 (WPARAM)(SF_RTF), (LPARAM)&es);
4367 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4369 /* !ST_SELECTION && !Unicode && \rtf */
4370 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4371 getText.codepage = 1200; /* no constant for unicode */
4372 getText.cb = MAX_BUF_LEN;
4373 getText.flags = GT_DEFAULT;
4374 getText.lpDefaultChar = NULL;
4375 getText.lpUsedDefChar = NULL;
4377 setText.flags = 0;
4378 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4379 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4380 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4381 ok(lstrcmpW(buf, TestItem1) == 0,
4382 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4384 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
4385 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
4386 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
4387 getText.codepage = CP_ACP;
4388 getText.cb = MAX_BUF_LEN;
4389 getText.flags = GT_DEFAULT;
4390 getText.lpDefaultChar = NULL;
4391 getText.lpUsedDefChar = NULL;
4393 setText.flags = ST_SELECTION;
4394 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4395 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
4396 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
4397 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4398 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
4400 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
4401 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4402 p = (char *)buf;
4403 es.dwCookie = (DWORD_PTR)&p;
4404 es.dwError = 0;
4405 es.pfnCallback = test_WM_SETTEXT_esCallback;
4406 memset(buf, 0, sizeof(buf));
4407 SendMessageA(hwndRichEdit, EM_STREAMOUT,
4408 (WPARAM)(SF_RTF), (LPARAM)&es);
4409 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4411 /* select some text */
4412 cr.cpMax = 1;
4413 cr.cpMin = 3;
4414 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4416 /* ST_SELECTION && !Unicode && \rtf */
4417 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4418 getText.codepage = 1200; /* no constant for unicode */
4419 getText.cb = MAX_BUF_LEN;
4420 getText.flags = GT_DEFAULT;
4421 getText.lpDefaultChar = NULL;
4422 getText.lpUsedDefChar = NULL;
4424 setText.flags = ST_SELECTION;
4425 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4426 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4427 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
4429 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
4430 setText.codepage = 1200; /* no constant for unicode */
4431 getText.codepage = CP_ACP;
4432 getText.cb = MAX_BUF_LEN;
4434 setText.flags = 0;
4435 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
4436 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4438 /* select some text */
4439 cr.cpMax = 1;
4440 cr.cpMin = 3;
4441 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4443 /* ST_SELECTION && !Unicode && !\rtf */
4444 setText.codepage = CP_ACP;
4445 getText.codepage = 1200; /* no constant for unicode */
4446 getText.cb = MAX_BUF_LEN;
4447 getText.flags = GT_DEFAULT;
4448 getText.lpDefaultChar = NULL;
4449 getText.lpUsedDefChar = NULL;
4451 setText.flags = ST_SELECTION;
4452 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
4453 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4454 ok(lstrcmpW(buf, TestItem1alt) == 0,
4455 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
4456 " using ST_SELECTION and non-Unicode\n");
4458 /* Test setting text using rich text format */
4459 setText.flags = 0;
4460 setText.codepage = CP_ACP;
4461 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
4462 getText.codepage = CP_ACP;
4463 getText.cb = MAX_BUF_LEN;
4464 getText.flags = GT_DEFAULT;
4465 getText.lpDefaultChar = NULL;
4466 getText.lpUsedDefChar = NULL;
4467 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4468 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
4470 setText.flags = 0;
4471 setText.codepage = CP_ACP;
4472 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
4473 getText.codepage = CP_ACP;
4474 getText.cb = MAX_BUF_LEN;
4475 getText.flags = GT_DEFAULT;
4476 getText.lpDefaultChar = NULL;
4477 getText.lpUsedDefChar = NULL;
4478 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4479 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
4481 /* test for utf8 text with BOM */
4482 setText.flags = 0;
4483 setText.codepage = CP_ACP;
4484 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4485 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4486 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4487 result = strcmp(bufACP, "TestUTF8WithBOM");
4488 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4490 setText.flags = 0;
4491 setText.codepage = CP_UTF8;
4492 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4493 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4494 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4495 result = strcmp(bufACP, "TestUTF8WithBOM");
4496 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4498 /* Test multibyte character */
4499 if (!is_lang_japanese)
4500 skip("Skip multibyte character tests on non-Japanese platform\n");
4501 else
4503 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4504 setText.flags = ST_SELECTION;
4505 setText.codepage = CP_ACP;
4506 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4507 todo_wine ok(result == 5, "EM_SETTEXTEX incorrectly returned %d, expected 5\n", result);
4508 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4509 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4510 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4511 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4513 setText.flags = ST_DEFAULT;
4514 setText.codepage = CP_ACP;
4515 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4516 ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4517 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4518 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4519 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4520 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4522 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4523 setText.flags = ST_SELECTION;
4524 setText.codepage = CP_ACP;
4525 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf abc\x8e\xf0}");
4526 todo_wine ok(result == 4, "EM_SETTEXTEX incorrectly returned %d, expected 4\n", result);
4527 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4528 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4529 todo_wine ok(!strcmp(bufACP, "abc\x8e\xf0"),
4530 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4533 DestroyWindow(hwndRichEdit);
4535 /* Single-line richedit */
4536 hwndRichEdit = new_richedit_with_style(NULL, 0);
4537 setText.flags = ST_DEFAULT;
4538 setText.codepage = CP_ACP;
4539 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"line1\r\nline2");
4540 ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4541 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4542 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4543 ok(!strcmp(bufACP, "line1"), "EM_SETTEXTEX: Test single-line text: Result: %s\n", bufACP);
4544 DestroyWindow(hwndRichEdit);
4547 static void test_EM_LIMITTEXT(void)
4549 int ret;
4551 HWND hwndRichEdit = new_richedit(NULL);
4553 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
4554 * about setting the length to -1 for multiline edit controls doesn't happen.
4557 /* Don't check default gettextlimit case. That's done in other tests */
4559 /* Set textlimit to 100 */
4560 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
4561 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4562 ok (ret == 100,
4563 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
4565 /* Set textlimit to 0 */
4566 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
4567 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4568 ok (ret == 65536,
4569 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
4571 /* Set textlimit to -1 */
4572 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
4573 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4574 ok (ret == -1,
4575 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
4577 /* Set textlimit to -2 */
4578 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
4579 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4580 ok (ret == -2,
4581 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
4583 DestroyWindow (hwndRichEdit);
4587 static void test_EM_EXLIMITTEXT(void)
4589 int i, selBegin, selEnd, len1, len2;
4590 int result;
4591 char text[1024 + 1];
4592 char buffer[1024 + 1];
4593 int textlimit = 0; /* multiple of 100 */
4594 HWND hwndRichEdit = new_richedit(NULL);
4596 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4597 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
4599 textlimit = 256000;
4600 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4601 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4602 /* set higher */
4603 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4605 textlimit = 1000;
4606 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4607 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4608 /* set lower */
4609 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4611 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4612 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4613 /* default for WParam = 0 */
4614 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4616 textlimit = sizeof(text)-1;
4617 memset(text, 'W', textlimit);
4618 text[sizeof(text)-1] = 0;
4619 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4620 /* maxed out text */
4621 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4623 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4624 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4625 len1 = selEnd - selBegin;
4627 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4628 SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4629 SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4630 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4631 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4632 len2 = selEnd - selBegin;
4634 ok(len1 != len2,
4635 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4636 len1,len2,i);
4638 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4639 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4640 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4641 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4642 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4643 len1 = selEnd - selBegin;
4645 ok(len1 != len2,
4646 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4647 len1,len2,i);
4649 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4650 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4651 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
4652 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4653 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4654 len2 = selEnd - selBegin;
4656 ok(len1 == len2,
4657 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4658 len1,len2,i);
4660 /* set text up to the limit, select all the text, then add a char */
4661 textlimit = 5;
4662 memset(text, 'W', textlimit);
4663 text[textlimit] = 0;
4664 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4665 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4666 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4667 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4668 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4669 result = strcmp(buffer, "A");
4670 ok(0 == result, "got string = \"%s\"\n", buffer);
4672 /* WM_SETTEXT not limited */
4673 textlimit = 10;
4674 memset(text, 'W', textlimit);
4675 text[textlimit] = 0;
4676 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4677 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4678 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4679 i = strlen(buffer);
4680 ok(10 == i, "expected 10 chars\n");
4681 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4682 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4684 /* try inserting more text at end */
4685 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4686 ok(0 == i, "WM_CHAR wasn't processed\n");
4687 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4688 i = strlen(buffer);
4689 ok(10 == i, "expected 10 chars, got %i\n", i);
4690 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4691 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4693 /* try inserting text at beginning */
4694 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4695 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4696 ok(0 == i, "WM_CHAR wasn't processed\n");
4697 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4698 i = strlen(buffer);
4699 ok(10 == i, "expected 10 chars, got %i\n", i);
4700 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4701 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4703 /* WM_CHAR is limited */
4704 textlimit = 1;
4705 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4706 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4707 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4708 ok(0 == i, "WM_CHAR wasn't processed\n");
4709 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4710 ok(0 == i, "WM_CHAR wasn't processed\n");
4711 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4712 i = strlen(buffer);
4713 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4715 DestroyWindow(hwndRichEdit);
4718 static void test_EM_GETLIMITTEXT(void)
4720 int i;
4721 HWND hwndRichEdit = new_richedit(NULL);
4723 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4724 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4726 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4727 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4728 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4730 DestroyWindow(hwndRichEdit);
4733 static void test_WM_SETFONT(void)
4735 /* There is no invalid input or error conditions for this function.
4736 * NULL wParam and lParam just fall back to their default values
4737 * It should be noted that even if you use a gibberish name for your fonts
4738 * here, it will still work because the name is stored. They will display as
4739 * System, but will report their name to be whatever they were created as */
4741 HWND hwndRichEdit = new_richedit(NULL);
4742 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4743 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4744 FF_DONTCARE, "Marlett");
4745 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4746 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4747 FF_DONTCARE, "MS Sans Serif");
4748 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4749 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4750 FF_DONTCARE, "Courier");
4751 LOGFONTA sentLogFont;
4752 CHARFORMAT2A returnedCF2A;
4754 returnedCF2A.cbSize = sizeof(returnedCF2A);
4756 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4757 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4758 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4760 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4761 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4762 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4763 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4765 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4766 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4767 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4768 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4769 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4770 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4772 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4773 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4774 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4775 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4776 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4777 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4779 /* This last test is special since we send in NULL. We clear the variables
4780 * and just compare to "System" instead of the sent in font name. */
4781 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4782 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4783 returnedCF2A.cbSize = sizeof(returnedCF2A);
4785 SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4786 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4787 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4788 ok (!strcmp("System",returnedCF2A.szFaceName),
4789 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4791 DestroyWindow(hwndRichEdit);
4795 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4796 LPBYTE pbBuff,
4797 LONG cb,
4798 LONG *pcb)
4800 const char** str = (const char**)dwCookie;
4801 int size = strlen(*str);
4802 if(size > 3) /* let's make it piecemeal for fun */
4803 size = 3;
4804 *pcb = cb;
4805 if (*pcb > size) {
4806 *pcb = size;
4808 if (*pcb > 0) {
4809 memcpy(pbBuff, *str, *pcb);
4810 *str += *pcb;
4812 return 0;
4815 #define open_clipboard(hwnd) open_clipboard_(__LINE__, hwnd)
4816 static BOOL open_clipboard_(int line, HWND hwnd)
4818 DWORD start = GetTickCount();
4819 while (1)
4821 BOOL ret = OpenClipboard(hwnd);
4822 if (ret || GetLastError() != ERROR_ACCESS_DENIED)
4823 return ret;
4824 if (GetTickCount() - start > 100)
4826 char classname[256];
4827 DWORD le = GetLastError();
4828 HWND clipwnd = GetOpenClipboardWindow();
4829 /* Provide a hint as to the source of interference:
4830 * - The class name would typically be CLIPBRDWNDCLASS if the
4831 * clipboard was opened by a Windows application using the
4832 * ole32 API.
4833 * - And it would be __wine_clipboard_manager if it was opened in
4834 * response to a native application.
4836 GetClassNameA(clipwnd, classname, ARRAY_SIZE(classname));
4837 trace_(__FILE__, line)("%p (%s) opened the clipboard\n", clipwnd, classname);
4838 SetLastError(le);
4839 return ret;
4841 Sleep(15);
4845 static void test_EM_GETMODIFY(void)
4847 HWND hwndRichEdit = new_richedit(NULL);
4848 LRESULT result;
4849 SETTEXTEX setText;
4850 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4851 'S', 'o', 'm', 'e',
4852 'T', 'e', 'x', 't', 0};
4853 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4854 'S', 'o', 'm', 'e',
4855 'O', 't', 'h', 'e', 'r',
4856 'T', 'e', 'x', 't', 0};
4857 const char* streamText = "hello world";
4858 CHARFORMAT2A cf2;
4859 PARAFORMAT2 pf2;
4860 EDITSTREAM es;
4861 BOOL r;
4862 HANDLE hclip;
4864 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4865 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4866 FF_DONTCARE, "Courier");
4868 setText.codepage = 1200; /* no constant for unicode */
4869 setText.flags = ST_KEEPUNDO;
4872 /* modify flag shouldn't be set when richedit is first created */
4873 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4874 ok (result == 0,
4875 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4877 /* setting modify flag should actually set it */
4878 SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4879 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4880 ok (result != 0,
4881 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4883 /* clearing modify flag should actually clear it */
4884 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4885 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4886 ok (result == 0,
4887 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4889 /* setting font doesn't change modify flag */
4890 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4891 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4892 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4893 ok (result == 0,
4894 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4896 /* setting text should set modify flag */
4897 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4898 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4899 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4900 ok (result != 0,
4901 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4903 /* undo previous text doesn't reset modify flag */
4904 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4905 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4906 ok (result != 0,
4907 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4909 /* set text with no flag to keep undo stack should not set modify flag */
4910 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4911 setText.flags = 0;
4912 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4913 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4914 ok (result == 0,
4915 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4917 /* WM_SETTEXT doesn't modify */
4918 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4919 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4920 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4921 ok (result == 0,
4922 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4924 /* clear the text */
4925 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4926 SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4927 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4928 ok (result == 0,
4929 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4931 /* replace text */
4932 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4933 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4934 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4935 SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4936 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4937 ok (result != 0,
4938 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4940 /* copy/paste text 1 */
4941 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4942 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4943 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4944 send_paste(hwndRichEdit);
4945 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4946 ok (result != 0,
4947 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4949 /* copy/paste text 2 */
4950 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4951 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4952 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4953 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4954 send_paste(hwndRichEdit);
4955 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4956 ok (result != 0,
4957 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4959 /* press char */
4960 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4961 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4962 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4963 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4964 ok (result != 0,
4965 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4967 /* press del */
4968 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4969 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4970 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4971 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4972 ok (result != 0,
4973 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4975 /* set char format */
4976 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4977 cf2.cbSize = sizeof(CHARFORMAT2A);
4978 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4979 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4980 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4981 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4982 result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4983 ok(result == 1, "EM_SETCHARFORMAT returned %Id instead of 1\n", result);
4984 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4985 ok (result != 0,
4986 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4988 /* set para format */
4989 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4990 pf2.cbSize = sizeof(PARAFORMAT2);
4991 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4992 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4993 pf2.wAlignment = PFA_RIGHT;
4994 SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4995 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4996 ok (result == 0,
4997 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4999 /* EM_STREAM */
5000 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
5001 es.dwCookie = (DWORD_PTR)&streamText;
5002 es.dwError = 0;
5003 es.pfnCallback = test_EM_GETMODIFY_esCallback;
5004 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5005 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
5006 ok (result != 0,
5007 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
5009 /* Check that the clipboard data is still available after destroying the
5010 * editor window.
5012 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Stayin' alive");
5013 SendMessageA(hwndRichEdit, EM_SETSEL, 8, -1);
5014 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
5016 DestroyWindow(hwndRichEdit);
5018 r = open_clipboard(NULL);
5019 ok(r, "OpenClipboard failed le=%lu\n", GetLastError());
5021 hclip = GetClipboardData(CF_TEXT);
5022 todo_wine ok(hclip != NULL, "GetClipboardData() failed le=%lu\n", GetLastError());
5023 if (hclip)
5025 const char* str = GlobalLock(hclip);
5026 ok(strcmp(str, "alive") == 0, "unexpected clipboard content: %s\n", str);
5027 GlobalUnlock(hclip);
5030 CloseClipboard();
5033 struct exsetsel_s {
5034 LONG min;
5035 LONG max;
5036 LRESULT expected_retval;
5037 int expected_getsel_start;
5038 int expected_getsel_end;
5039 BOOL todo;
5042 static const struct exsetsel_s exsetsel_tests[] = {
5043 /* sanity tests */
5044 {5, 10, 10, 5, 10 },
5045 {15, 17, 17, 15, 17 },
5046 /* test cpMax > strlen() */
5047 {0, 100, 18, 0, 18 },
5048 /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
5049 {-1, 1, 17, 17, 17 },
5050 /* test cpMin == cpMax */
5051 {5, 5, 5, 5, 5 },
5052 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
5053 {-1, 0, 5, 5, 5 },
5054 {-1, 17, 5, 5, 5 },
5055 {-1, 18, 5, 5, 5 },
5056 /* test cpMin < 0 && cpMax < 0 */
5057 {-1, -1, 17, 17, 17 },
5058 {-4, -5, 17, 17, 17 },
5059 /* test cpMin >=0 && cpMax < 0 (bug 6814) */
5060 {0, -1, 18, 0, 18 },
5061 {17, -5, 18, 17, 18 },
5062 {18, -3, 17, 17, 17 },
5063 /* test if cpMin > cpMax */
5064 {15, 19, 18, 15, 18 },
5065 {19, 15, 18, 15, 18 },
5066 /* cpMin == strlen() && cpMax > cpMin */
5067 {17, 18, 18, 17, 18 },
5068 {17, 50, 18, 17, 18 },
5071 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
5072 CHARRANGE cr;
5073 LRESULT result;
5074 int start, end;
5076 cr.cpMin = setsel->min;
5077 cr.cpMax = setsel->max;
5078 result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
5080 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %Id actual: %Id\n", id, setsel->expected_retval, result);
5082 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
5084 todo_wine_if (setsel->todo)
5085 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
5086 id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
5089 static void test_EM_EXSETSEL(void)
5091 HWND hwndRichEdit = new_richedit(NULL);
5092 int i;
5093 const int num_tests = ARRAY_SIZE(exsetsel_tests);
5095 /* sending some text to the window */
5096 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
5097 /* 01234567890123456*/
5098 /* 10 */
5100 for (i = 0; i < num_tests; i++) {
5101 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
5104 if (!is_lang_japanese)
5105 skip("Skip multibyte character tests on non-Japanese platform\n");
5106 else
5108 CHARRANGE cr;
5109 char bufA[MAX_BUF_LEN] = {0};
5110 LRESULT result;
5112 /* Test with multibyte character */
5113 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
5114 /* 012345 6 78901 */
5115 cr.cpMin = 4; cr.cpMax = 8;
5116 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
5117 ok(result == 8, "EM_EXSETSEL return %Id expected 8\n", result);
5118 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)bufA);
5119 ok(!strcmp(bufA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
5120 SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5121 ok(cr.cpMin == 4, "Selection start incorrectly: %ld expected 4\n", cr.cpMin);
5122 ok(cr.cpMax == 8, "Selection end incorrectly: %ld expected 8\n", cr.cpMax);
5125 DestroyWindow(hwndRichEdit);
5128 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
5129 LRESULT result;
5130 int start, end;
5132 result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
5134 ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %Id actual: %Id\n", id, setsel->expected_retval, result);
5136 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
5138 todo_wine_if (setsel->todo)
5139 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
5140 id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
5143 /* When the selection is out of the windows view, the scrollbar should move. */
5144 static void check_EM_SETSEL_multiline(HWND hwnd)
5146 int oldY;
5147 int curY;
5148 const char textwithlines[] = "This is a text\n"
5149 "with lines\n"
5150 "I expect this text\n"
5151 "to be\nlarge\nenough\n";
5153 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)textwithlines);
5154 oldY = get_scroll_pos_y(hwnd);
5155 SendMessageA(hwnd, EM_SETSEL, 59, 59);
5156 curY = get_scroll_pos_y(hwnd);
5157 ok(oldY < curY, "oldY %d >= curY %d\n", oldY, curY);
5160 static void test_EM_SETSEL(void)
5162 char buffA[32] = {0};
5163 HWND hwndRichEdit = new_richedit(NULL);
5164 int i;
5165 const int num_tests = ARRAY_SIZE(exsetsel_tests);
5167 /* sending some text to the window */
5168 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
5169 /* 01234567890123456*/
5170 /* 10 */
5172 for (i = 0; i < num_tests; i++) {
5173 check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
5176 SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
5177 buffA[0] = 123;
5178 SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
5179 ok(buffA[0] == 0, "selection text %s\n", buffA);
5181 if (!is_lang_japanese)
5182 skip("Skip multibyte character tests on non-Japanese platform\n");
5183 else
5185 int sel_start, sel_end;
5186 LRESULT result;
5188 /* Test with multibyte character */
5189 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
5190 /* 012345 6 78901 */
5191 result = SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
5192 ok(result == 8, "EM_SETSEL return %Id expected 8\n", result);
5193 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
5194 ok(!strcmp(buffA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
5195 result = SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5196 ok(sel_start == 4, "Selection start incorrectly: %d expected 4\n", sel_start);
5197 ok(sel_end == 8, "Selection end incorrectly: %d expected 8\n", sel_end);
5200 check_EM_SETSEL_multiline(hwndRichEdit);
5202 DestroyWindow(hwndRichEdit);
5205 static void test_EM_REPLACESEL(int redraw)
5207 HWND hwndRichEdit = new_richedit(NULL);
5208 char buffer[1024] = {0};
5209 int r;
5210 GETTEXTEX getText;
5211 CHARRANGE cr;
5212 CHAR rtfstream[] = "{\\rtf1 TestSomeText}";
5213 CHAR urtfstream[] = "{\\urtf1 TestSomeText}";
5215 /* sending some text to the window */
5216 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
5217 /* 01234567890123456*/
5218 /* 10 */
5220 /* FIXME add more tests */
5221 SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
5222 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
5223 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
5224 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5225 r = strcmp(buffer, "testing");
5226 ok(0 == r, "expected %d, got %d\n", 0, r);
5228 DestroyWindow(hwndRichEdit);
5230 hwndRichEdit = new_richedit(NULL);
5232 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
5233 SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
5235 /* Test behavior with carriage returns and newlines */
5236 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5237 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
5238 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
5239 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5240 r = strcmp(buffer, "RichEdit1");
5241 ok(0 == r, "expected %d, got %d\n", 0, r);
5242 getText.cb = 1024;
5243 getText.codepage = CP_ACP;
5244 getText.flags = GT_DEFAULT;
5245 getText.lpDefaultChar = NULL;
5246 getText.lpUsedDefChar = NULL;
5247 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5248 ok(strcmp(buffer, "RichEdit1") == 0,
5249 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
5251 /* Test number of lines reported after EM_REPLACESEL */
5252 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5253 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5255 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5256 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
5257 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
5258 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5259 r = strcmp(buffer, "RichEdit1\r\n");
5260 ok(0 == r, "expected %d, got %d\n", 0, r);
5261 getText.cb = 1024;
5262 getText.codepage = CP_ACP;
5263 getText.flags = GT_DEFAULT;
5264 getText.lpDefaultChar = NULL;
5265 getText.lpUsedDefChar = NULL;
5266 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5267 ok(strcmp(buffer, "RichEdit1\r") == 0,
5268 "EM_GETTEXTEX returned incorrect string\n");
5270 /* Test number of lines reported after EM_REPLACESEL */
5271 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5272 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5274 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5275 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
5276 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
5278 /* Test number of lines reported after EM_REPLACESEL */
5279 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5280 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5282 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5283 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5284 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%ld, expected 10\n", cr.cpMin);
5285 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%ld, expected 10\n", cr.cpMax);
5287 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5288 r = strcmp(buffer, "RichEdit1\r\n");
5289 ok(0 == r, "expected %d, got %d\n", 0, r);
5290 getText.cb = 1024;
5291 getText.codepage = CP_ACP;
5292 getText.flags = GT_DEFAULT;
5293 getText.lpDefaultChar = NULL;
5294 getText.lpUsedDefChar = NULL;
5295 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5296 ok(strcmp(buffer, "RichEdit1\r") == 0,
5297 "EM_GETTEXTEX returned incorrect string\n");
5299 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5300 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5301 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%ld, expected 10\n", cr.cpMin);
5302 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%ld, expected 10\n", cr.cpMax);
5304 /* The following tests show that richedit should handle the special \r\r\n
5305 sequence by turning it into a single space on insertion. However,
5306 EM_REPLACESEL on WinXP returns the number of characters in the original
5307 string.
5310 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5311 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
5312 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
5313 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5314 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5315 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%ld, expected 2\n", cr.cpMin);
5316 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%ld, expected 2\n", cr.cpMax);
5318 /* Test the actual string */
5319 getText.cb = 1024;
5320 getText.codepage = CP_ACP;
5321 getText.flags = GT_DEFAULT;
5322 getText.lpDefaultChar = NULL;
5323 getText.lpUsedDefChar = NULL;
5324 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5325 ok(strcmp(buffer, "\r\r") == 0,
5326 "EM_GETTEXTEX returned incorrect string\n");
5328 /* Test number of lines reported after EM_REPLACESEL */
5329 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5330 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5332 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5333 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
5334 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
5335 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5336 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5337 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%ld, expected 1\n", cr.cpMin);
5338 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%ld, expected 1\n", cr.cpMax);
5340 /* Test the actual string */
5341 getText.cb = 1024;
5342 getText.codepage = CP_ACP;
5343 getText.flags = GT_DEFAULT;
5344 getText.lpDefaultChar = NULL;
5345 getText.lpUsedDefChar = NULL;
5346 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5347 ok(strcmp(buffer, " ") == 0,
5348 "EM_GETTEXTEX returned incorrect string\n");
5350 /* Test number of lines reported after EM_REPLACESEL */
5351 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5352 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
5354 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5355 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
5356 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5357 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5358 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5359 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%ld, expected 7\n", cr.cpMin);
5360 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%ld, expected 7\n", cr.cpMax);
5362 /* Test the actual string */
5363 getText.cb = 1024;
5364 getText.codepage = CP_ACP;
5365 getText.flags = GT_DEFAULT;
5366 getText.lpDefaultChar = NULL;
5367 getText.lpUsedDefChar = NULL;
5368 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5369 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
5370 "EM_GETTEXTEX returned incorrect string\n");
5372 /* Test number of lines reported after EM_REPLACESEL */
5373 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5374 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5376 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5377 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
5378 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5379 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5380 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5381 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%ld, expected 2\n", cr.cpMin);
5382 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%ld, expected 2\n", cr.cpMax);
5384 /* Test the actual string */
5385 getText.cb = 1024;
5386 getText.codepage = CP_ACP;
5387 getText.flags = GT_DEFAULT;
5388 getText.lpDefaultChar = NULL;
5389 getText.lpUsedDefChar = NULL;
5390 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5391 ok(strcmp(buffer, " \r") == 0,
5392 "EM_GETTEXTEX returned incorrect string\n");
5394 /* Test number of lines reported after EM_REPLACESEL */
5395 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5396 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
5398 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5399 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
5400 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5401 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5402 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5403 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%ld, expected 3\n", cr.cpMin);
5404 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%ld, expected 3\n", cr.cpMax);
5406 /* Test the actual string */
5407 getText.cb = 1024;
5408 getText.codepage = CP_ACP;
5409 getText.flags = GT_DEFAULT;
5410 getText.lpDefaultChar = NULL;
5411 getText.lpUsedDefChar = NULL;
5412 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5413 ok(strcmp(buffer, " \r\r") == 0,
5414 "EM_GETTEXTEX returned incorrect string\n");
5416 /* Test number of lines reported after EM_REPLACESEL */
5417 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5418 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5420 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5421 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
5422 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
5423 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5424 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5425 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%ld, expected 5\n", cr.cpMin);
5426 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%ld, expected 5\n", cr.cpMax);
5428 /* Test the actual string */
5429 getText.cb = 1024;
5430 getText.codepage = CP_ACP;
5431 getText.flags = GT_DEFAULT;
5432 getText.lpDefaultChar = NULL;
5433 getText.lpUsedDefChar = NULL;
5434 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5435 ok(strcmp(buffer, "\rX\r\r\r") == 0,
5436 "EM_GETTEXTEX returned incorrect string\n");
5438 /* Test number of lines reported after EM_REPLACESEL */
5439 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5440 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
5442 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5443 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
5444 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
5445 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5446 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5447 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%ld, expected 2\n", cr.cpMin);
5448 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%ld, expected 2\n", cr.cpMax);
5450 /* Test the actual string */
5451 getText.cb = 1024;
5452 getText.codepage = CP_ACP;
5453 getText.flags = GT_DEFAULT;
5454 getText.lpDefaultChar = NULL;
5455 getText.lpUsedDefChar = NULL;
5456 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5457 ok(strcmp(buffer, "\r\r") == 0,
5458 "EM_GETTEXTEX returned incorrect string\n");
5460 /* Test number of lines reported after EM_REPLACESEL */
5461 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5462 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5464 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5465 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
5466 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5467 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5468 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5469 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%ld, expected 7\n", cr.cpMin);
5470 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%ld, expected 7\n", cr.cpMax);
5472 /* Test the actual string */
5473 getText.cb = 1024;
5474 getText.codepage = CP_ACP;
5475 getText.flags = GT_DEFAULT;
5476 getText.lpDefaultChar = NULL;
5477 getText.lpUsedDefChar = NULL;
5478 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5479 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
5480 "EM_GETTEXTEX returned incorrect string\n");
5482 /* Test number of lines reported after EM_REPLACESEL */
5483 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5484 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5486 /* Test with multibyte character */
5487 if (!is_lang_japanese)
5488 skip("Skip multibyte character tests on non-Japanese platform\n");
5489 else
5491 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5492 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"abc\x8e\xf0");
5493 todo_wine ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5494 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5495 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5496 ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%ld, expected 4\n", cr.cpMin);
5497 ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%ld, expected 4\n", cr.cpMax);
5498 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5499 ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5500 ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5502 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5503 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"{\\rtf abc\x8e\xf0}");
5504 todo_wine ok(r == 4, "EM_REPLACESEL returned %d, expected 4\n", r);
5505 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5506 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5507 todo_wine ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%ld, expected 4\n", cr.cpMin);
5508 todo_wine ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%ld, expected 4\n", cr.cpMax);
5509 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5510 todo_wine ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5511 todo_wine ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5514 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5515 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5516 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5517 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5518 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5519 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%ld, expected 12\n", cr.cpMin);
5520 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%ld, expected 12\n", cr.cpMax);
5521 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5522 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5524 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5525 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urtfstream);
5526 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5527 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5528 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5529 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%ld, expected 12\n", cr.cpMin);
5530 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%ld, expected 12\n", cr.cpMax);
5531 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5532 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5534 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Wine");
5535 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5536 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5537 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5538 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5539 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5540 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%ld, expected 13\n", cr.cpMin);
5541 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%ld, expected 13\n", cr.cpMax);
5542 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5543 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5545 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 Wine}");
5546 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5547 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5548 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5549 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5550 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5551 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%ld, expected 13\n", cr.cpMin);
5552 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%ld, expected 13\n", cr.cpMax);
5553 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5554 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5556 if (!redraw)
5557 /* This is needed to avoid interfering with keybd_event calls
5558 * on other tests that simulate keyboard events. */
5559 SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
5561 DestroyWindow(hwndRichEdit);
5563 /* Single-line richedit */
5564 hwndRichEdit = new_richedit_with_style(NULL, 0);
5565 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"line1\r\nline2");
5566 ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5567 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5568 ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5569 ok(!strcmp(buffer, "line1"), "WM_GETTEXT returned incorrect string '%s'\n", buffer);
5570 DestroyWindow(hwndRichEdit);
5573 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
5574 * to test the state of the modifiers (Ctrl/Alt/Shift).
5576 * Therefore Ctrl-<key> keystrokes need to be simulated with
5577 * keybd_event or by using SetKeyboardState to set the modifiers
5578 * and SendMessage to simulate the keystrokes.
5580 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
5582 LRESULT result;
5583 hold_key(VK_CONTROL);
5584 result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
5585 release_key(VK_CONTROL);
5586 return result;
5589 static void test_WM_PASTE(void)
5591 int result;
5592 char buffer[1024] = {0};
5593 const char* text1 = "testing paste\r";
5594 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
5595 const char* text1_after = "testing paste\r\n";
5596 const char* text2 = "testing paste\r\rtesting paste";
5597 const char* text2_after = "testing paste\r\n\r\ntesting paste";
5598 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
5599 HWND hwndRichEdit = new_richedit(NULL);
5601 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5602 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
5604 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
5605 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5606 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
5607 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5608 /* Pasted text should be visible at this step */
5609 result = strcmp(text1_step1, buffer);
5610 ok(result == 0,
5611 "test paste: strcmp = %i, text='%s'\n", result, buffer);
5613 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
5614 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5615 /* Text should be the same as before (except for \r -> \r\n conversion) */
5616 result = strcmp(text1_after, buffer);
5617 ok(result == 0,
5618 "test paste: strcmp = %i, text='%s'\n", result, buffer);
5620 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
5621 SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
5622 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
5623 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5624 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
5625 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5626 /* Pasted text should be visible at this step */
5627 result = strcmp(text3, buffer);
5628 ok(result == 0,
5629 "test paste: strcmp = %i\n", result);
5630 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
5631 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5632 /* Text should be the same as before (except for \r -> \r\n conversion) */
5633 result = strcmp(text2_after, buffer);
5634 ok(result == 0,
5635 "test paste: strcmp = %i\n", result);
5636 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
5637 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5638 /* Text should revert to post-paste state */
5639 result = strcmp(buffer,text3);
5640 ok(result == 0,
5641 "test paste: strcmp = %i\n", result);
5643 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5644 /* Send WM_CHAR to simulate Ctrl-V */
5645 SendMessageA(hwndRichEdit, WM_CHAR, 22,
5646 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5647 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5648 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
5649 result = strcmp(buffer,"");
5650 ok(result == 0,
5651 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5653 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
5654 * with SetKeyboard state. */
5656 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5657 /* Simulates paste (Ctrl-V) */
5658 hold_key(VK_CONTROL);
5659 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
5660 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5661 release_key(VK_CONTROL);
5662 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5663 result = strcmp(buffer,"paste");
5664 ok(result == 0,
5665 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5667 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5668 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
5669 /* Simulates copy (Ctrl-C) */
5670 hold_key(VK_CONTROL);
5671 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
5672 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
5673 release_key(VK_CONTROL);
5674 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5675 send_paste(hwndRichEdit);
5676 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5677 result = strcmp(buffer,"testing");
5678 ok(result == 0,
5679 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5681 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
5682 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
5683 /* Simulates select all (Ctrl-A) */
5684 hold_key(VK_CONTROL);
5685 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
5686 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
5687 /* Simulates select cut (Ctrl-X) */
5688 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
5689 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
5690 release_key(VK_CONTROL);
5691 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5692 result = strcmp(buffer,"");
5693 ok(result == 0,
5694 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5695 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5696 send_paste(hwndRichEdit);
5697 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5698 result = strcmp(buffer,"cut\r\n");
5699 ok(result == 0,
5700 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5701 /* Simulates undo (Ctrl-Z) */
5702 hold_key(VK_CONTROL);
5703 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
5704 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
5705 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5706 result = strcmp(buffer,"");
5707 ok(result == 0,
5708 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5709 /* Simulates redo (Ctrl-Y) */
5710 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
5711 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
5712 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5713 result = strcmp(buffer,"cut\r\n");
5714 ok(result == 0,
5715 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5716 release_key(VK_CONTROL);
5718 /* Copy multiline text to clipboard for future use */
5719 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
5720 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
5721 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
5722 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
5724 /* Paste into read-only control */
5725 result = SendMessageA(hwndRichEdit, EM_SETREADONLY, TRUE, 0);
5726 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5727 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5728 result = strcmp(buffer, text3);
5729 ok(result == 0,
5730 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5732 /* Cut from read-only control */
5733 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
5734 SendMessageA(hwndRichEdit, WM_CUT, 0, 0);
5735 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5736 result = strcmp(buffer, text3);
5737 ok(result == 0,
5738 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5740 /* FIXME: Wine doesn't flush Ole clipboard when window is destroyed so do it manually */
5741 OleFlushClipboard();
5742 DestroyWindow(hwndRichEdit);
5744 /* Paste multi-line text into single-line control */
5745 hwndRichEdit = new_richedit_with_style(NULL, 0);
5746 send_paste(hwndRichEdit);
5747 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5748 result = strcmp(buffer, "testing paste");
5749 ok(result == 0,
5750 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5751 DestroyWindow(hwndRichEdit);
5754 static void test_EM_FORMATRANGE(void)
5756 int r, i, tpp_x, tpp_y;
5757 HDC hdc;
5758 HWND hwndRichEdit = new_richedit(NULL);
5759 FORMATRANGE fr;
5760 BOOL skip_non_english;
5761 static const struct {
5762 const char *string; /* The string */
5763 int first; /* First 'pagebreak', 0 for don't care */
5764 int second; /* Second 'pagebreak', 0 for don't care */
5765 } fmtstrings[] = {
5766 {"WINE wine", 0, 0},
5767 {"WINE wineWine", 0, 0},
5768 {"WINE\r\nwine\r\nwine", 5, 10},
5769 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
5770 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
5773 skip_non_english = (PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH);
5774 if (skip_non_english)
5775 skip("Skipping some tests on non-English platform\n");
5777 hdc = GetDC(hwndRichEdit);
5778 ok(hdc != NULL, "Could not get HDC\n");
5780 /* Calculate the twips per pixel */
5781 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
5782 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
5784 /* Test the simple case where all the text fits in the page rect. */
5785 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
5786 fr.hdc = fr.hdcTarget = hdc;
5787 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5788 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
5789 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
5790 fr.chrg.cpMin = 0;
5791 fr.chrg.cpMax = -1;
5792 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5793 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
5795 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
5796 fr.rc.bottom = fr.rcPage.bottom;
5797 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5798 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
5800 SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
5802 for (i = 0; i < ARRAY_SIZE(fmtstrings); i++)
5804 GETTEXTLENGTHEX gtl;
5805 SIZE stringsize;
5806 int len;
5808 winetest_push_context("%d", i);
5809 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
5811 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5812 gtl.codepage = CP_ACP;
5813 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5815 /* Get some size information for the string */
5816 GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
5818 /* Define the box to be half the width needed and a bit larger than the height.
5819 * Changes to the width means we have at least 2 pages. Changes to the height
5820 * is done so we can check the changing of fr.rc.bottom.
5822 fr.hdc = fr.hdcTarget = hdc;
5823 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5824 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
5825 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
5827 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5828 todo_wine {
5829 ok(r == len, "Expected %d, got %d\n", len, r);
5832 /* We know that the page can't hold the full string. See how many characters
5833 * are on the first one
5835 fr.chrg.cpMin = 0;
5836 fr.chrg.cpMax = -1;
5837 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5838 todo_wine {
5839 if (! skip_non_english)
5840 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %ld, got %ld\n", (stringsize.cy * tpp_y), fr.rc.bottom);
5842 if (fmtstrings[i].first)
5843 todo_wine {
5844 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
5846 else
5847 ok(r < len, "Expected < %d, got %d\n", len, r);
5849 /* Do another page */
5850 fr.chrg.cpMin = r;
5851 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5852 if (fmtstrings[i].second)
5853 todo_wine {
5854 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
5856 else if (! skip_non_english)
5857 ok (r < len, "Expected < %d, got %d\n", len, r);
5859 /* There is at least one more page, but we don't care */
5861 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5862 todo_wine {
5863 ok(r == len, "Expected %d, got %d\n", len, r);
5865 winetest_pop_context();
5868 ReleaseDC(NULL, hdc);
5869 DestroyWindow(hwndRichEdit);
5872 static int nCallbackCount = 0;
5874 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5875 LONG cb, LONG* pcb)
5877 const char text[] = {'t','e','s','t'};
5879 if (sizeof(text) <= cb)
5881 if ((int)dwCookie != nCallbackCount)
5883 *pcb = 0;
5884 return 0;
5887 memcpy (pbBuff, text, sizeof(text));
5888 *pcb = sizeof(text);
5890 nCallbackCount++;
5892 return 0;
5894 else
5895 return 1; /* indicates callback failed */
5898 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5899 LPBYTE pbBuff,
5900 LONG cb,
5901 LONG *pcb)
5903 const char** str = (const char**)dwCookie;
5904 int size = strlen(*str);
5905 *pcb = cb;
5906 if (*pcb > size) {
5907 *pcb = size;
5909 if (*pcb > 0) {
5910 memcpy(pbBuff, *str, *pcb);
5911 *str += *pcb;
5913 return 0;
5916 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5917 LPBYTE pbBuff,
5918 LONG cb,
5919 LONG *pcb)
5921 DWORD *phase = (DWORD *)dwCookie;
5923 if(*phase == 0){
5924 static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5925 *pcb = sizeof(first) - 1;
5926 memcpy(pbBuff, first, *pcb);
5927 }else if(*phase == 1){
5928 static const char second[] = "\x8f\xc3\x8b";
5929 *pcb = sizeof(second) - 1;
5930 memcpy(pbBuff, second, *pcb);
5931 }else
5932 *pcb = 0;
5934 ++*phase;
5936 return 0;
5939 static DWORD CALLBACK test_EM_STREAMIN_null_bytes(DWORD_PTR cookie, BYTE *buf, LONG size, LONG *written)
5941 DWORD *phase = (DWORD *)cookie;
5943 if (*phase == 0)
5945 static const char first[] = "{\\rtf1\\ansi{Th\0is";
5946 *written = sizeof(first);
5947 memcpy(buf, first, *written);
5949 else if (*phase == 1)
5951 static const char second[] = " is a test}}";
5952 *written = sizeof(second);
5953 memcpy(buf, second, *written);
5955 else
5956 *written = 0;
5958 ++*phase;
5960 return 0;
5963 struct StringWithLength {
5964 int length;
5965 char *buffer;
5968 /* This callback is used to handled the null characters in a string. */
5969 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5970 LPBYTE pbBuff,
5971 LONG cb,
5972 LONG *pcb)
5974 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5975 int size = str->length;
5976 *pcb = cb;
5977 if (*pcb > size) {
5978 *pcb = size;
5980 if (*pcb > 0) {
5981 memcpy(pbBuff, str->buffer, *pcb);
5982 str->buffer += *pcb;
5983 str->length -= *pcb;
5985 return 0;
5988 static void test_EM_STREAMIN(void)
5990 HWND hwndRichEdit = new_richedit(NULL);
5991 DWORD phase;
5992 LRESULT result;
5993 EDITSTREAM es;
5994 char buffer[1024] = {0}, tmp[16];
5995 CHARRANGE range;
5996 PARAFORMAT2 fmt;
5997 DWORD len;
5999 const char * streamText0 = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText}";
6000 const char * streamText0a = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText\\par}";
6001 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
6002 const char * ptr;
6004 const char * streamText1 =
6005 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
6006 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
6007 "}\r\n";
6009 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
6010 const char * streamText2 =
6011 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
6012 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
6013 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
6014 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
6015 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
6016 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
6017 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
6019 const char * streamText3 = "RichEdit1";
6021 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
6023 const char * streamText4 =
6024 "This text just needs to be long enough to cause run to be split onto "
6025 "two separate lines and make sure the null terminating character is "
6026 "handled properly.\0";
6028 const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
6030 int length4 = strlen(streamText4) + 1;
6031 struct StringWithLength cookieForStream4 = {
6032 length4,
6033 (char *)streamText4,
6036 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
6037 int length5 = ARRAY_SIZE(streamText5);
6038 struct StringWithLength cookieForStream5 = {
6039 sizeof(streamText5),
6040 (char *)streamText5,
6043 /* Minimal test without \par at the end */
6044 es.dwCookie = (DWORD_PTR)&streamText0;
6045 es.dwError = 0;
6046 es.pfnCallback = test_EM_STREAMIN_esCallback;
6047 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6048 ok(result == 12, "got %Id, expected %d\n", result, 12);
6050 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6051 ok (result == 12,
6052 "EM_STREAMIN: Test 0 returned %Id, expected 12\n", result);
6053 result = strcmp (buffer,"TestSomeText");
6054 ok (result == 0,
6055 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
6056 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %ld, expected %d\n", es.dwError, 0);
6057 /* Show that para fmts are ignored */
6058 range.cpMin = 2;
6059 range.cpMax = 2;
6060 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
6061 memset(&fmt, 0xcc, sizeof(fmt));
6062 fmt.cbSize = sizeof(fmt);
6063 result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
6064 ok(fmt.dxStartIndent == 0, "got %ld\n", fmt.dxStartIndent);
6065 ok(fmt.dxOffset == 0, "got %ld\n", fmt.dxOffset);
6066 ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
6067 ok((fmt.wEffects & PFE_RTLPARA) == 0, "got %x\n", fmt.wEffects);
6069 /* Native richedit 2.0 ignores last \par */
6070 ptr = streamText0a;
6071 es.dwCookie = (DWORD_PTR)&ptr;
6072 es.dwError = 0;
6073 es.pfnCallback = test_EM_STREAMIN_esCallback;
6074 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6075 ok(result == 12, "got %Id, expected %d\n", result, 12);
6077 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6078 ok (result == 12,
6079 "EM_STREAMIN: Test 0-a returned %Id, expected 12\n", result);
6080 result = strcmp (buffer,"TestSomeText");
6081 ok (result == 0,
6082 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
6083 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %ld, expected %d\n", es.dwError, 0);
6084 /* This time para fmts are processed */
6085 range.cpMin = 2;
6086 range.cpMax = 2;
6087 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
6088 memset(&fmt, 0xcc, sizeof(fmt));
6089 fmt.cbSize = sizeof(fmt);
6090 result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
6091 ok(fmt.dxStartIndent == 300, "got %ld\n", fmt.dxStartIndent);
6092 ok(fmt.dxOffset == -100, "got %ld\n", fmt.dxOffset);
6093 ok(fmt.wAlignment == PFA_RIGHT, "got %d\n", fmt.wAlignment);
6094 ok((fmt.wEffects & PFE_RTLPARA) == PFE_RTLPARA, "got %x\n", fmt.wEffects);
6096 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
6097 es.dwCookie = (DWORD_PTR)&streamText0b;
6098 es.dwError = 0;
6099 es.pfnCallback = test_EM_STREAMIN_esCallback;
6100 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6101 ok(result == 13, "got %Id, expected %d\n", result, 13);
6103 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6104 ok (result == 14,
6105 "EM_STREAMIN: Test 0-b returned %Id, expected 14\n", result);
6106 result = strcmp (buffer,"TestSomeText\r\n");
6107 ok (result == 0,
6108 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
6109 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %ld, expected %d\n", es.dwError, 0);
6111 /* Show that when using SFF_SELECTION the last \par is not ignored. */
6112 ptr = streamText0a;
6113 es.dwCookie = (DWORD_PTR)&ptr;
6114 es.dwError = 0;
6115 es.pfnCallback = test_EM_STREAMIN_esCallback;
6116 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6117 ok(result == 12, "got %Id, expected %d\n", result, 12);
6119 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6120 ok (result == 12,
6121 "EM_STREAMIN: Test 0-a returned %Id, expected 12\n", result);
6122 result = strcmp (buffer,"TestSomeText");
6123 ok (result == 0,
6124 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
6125 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %ld, expected %d\n", es.dwError, 0);
6127 range.cpMin = 0;
6128 range.cpMax = -1;
6129 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
6130 ok (result == 13, "got %Id\n", result);
6132 ptr = streamText0a;
6133 es.dwCookie = (DWORD_PTR)&ptr;
6134 es.dwError = 0;
6135 es.pfnCallback = test_EM_STREAMIN_esCallback;
6137 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
6138 ok(result == 13, "got %Id, expected 13\n", result);
6140 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6141 ok (result == 14,
6142 "EM_STREAMIN: Test SFF_SELECTION 0-a returned %Id, expected 14\n", result);
6143 result = strcmp (buffer,"TestSomeText\r\n");
6144 ok (result == 0,
6145 "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
6146 ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %ld, expected %d\n", es.dwError, 0);
6148 es.dwCookie = (DWORD_PTR)&streamText1;
6149 es.dwError = 0;
6150 es.pfnCallback = test_EM_STREAMIN_esCallback;
6151 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6152 ok(result == 12, "got %Id, expected %d\n", result, 12);
6154 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6155 ok (result == 12,
6156 "EM_STREAMIN: Test 1 returned %Id, expected 12\n", result);
6157 result = strcmp (buffer,"TestSomeText");
6158 ok (result == 0,
6159 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
6160 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %ld, expected %d\n", es.dwError, 0);
6162 es.dwCookie = (DWORD_PTR)&streamText2;
6163 es.dwError = 0;
6164 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6165 ok(result == 0, "got %Id, expected %d\n", result, 0);
6167 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6168 ok (result == 0,
6169 "EM_STREAMIN: Test 2 returned %Id, expected 0\n", result);
6170 ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6171 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %ld, expected %d\n", es.dwError, -16);
6173 es.dwCookie = (DWORD_PTR)&streamText3;
6174 es.dwError = 0;
6175 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6176 ok(result == 0, "got %Id, expected %d\n", result, 0);
6178 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6179 ok (result == 0,
6180 "EM_STREAMIN: Test 3 returned %Id, expected 0\n", result);
6181 ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
6182 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %ld, expected %d\n", es.dwError, -16);
6184 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
6185 es.dwError = 0;
6186 es.pfnCallback = test_EM_STREAMIN_esCallback;
6187 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6188 ok(result == 18, "got %Id, expected %d\n", result, 18);
6190 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6191 ok(result == 15,
6192 "EM_STREAMIN: Test UTF8WithBOM returned %Id, expected 15\n", result);
6193 result = strcmp (buffer,"TestUTF8WithBOM");
6194 ok(result == 0,
6195 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
6196 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %ld, expected %d\n", es.dwError, 0);
6198 phase = 0;
6199 es.dwCookie = (DWORD_PTR)&phase;
6200 es.dwError = 0;
6201 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
6202 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6203 ok(result == 8, "got %Id\n", result);
6205 len = WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
6207 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6208 ok(result + 1 == len,
6209 "EM_STREAMIN: Test UTF8Split returned %Id\n", result);
6210 result = memcmp (buffer, tmp, result);
6211 ok(result == 0,
6212 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
6213 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %ld, expected %d\n", es.dwError, 0);
6215 es.dwCookie = (DWORD_PTR)&cookieForStream4;
6216 es.dwError = 0;
6217 es.pfnCallback = test_EM_STREAMIN_esCallback2;
6218 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6219 ok(result == length4, "got %Id, expected %d\n", result, length4);
6221 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6222 ok (result == length4,
6223 "EM_STREAMIN: Test 4 returned %Id, expected %d\n", result, length4);
6224 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %ld, expected %d\n", es.dwError, 0);
6226 es.dwCookie = (DWORD_PTR)&cookieForStream5;
6227 es.dwError = 0;
6228 es.pfnCallback = test_EM_STREAMIN_esCallback2;
6229 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
6230 ok(result == sizeof(streamText5), "got %Id, expected %u\n", result, (UINT)sizeof(streamText5));
6232 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6233 ok (result == length5,
6234 "EM_STREAMIN: Test 5 returned %Id, expected %d\n", result, length5);
6235 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %ld, expected %d\n", es.dwError, 0);
6237 DestroyWindow(hwndRichEdit);
6239 /* Single-line richedit */
6240 hwndRichEdit = new_richedit_with_style(NULL, 0);
6241 ptr = "line1\r\nline2";
6242 es.dwCookie = (DWORD_PTR)&ptr;
6243 es.dwError = 0;
6244 es.pfnCallback = test_EM_STREAMIN_esCallback;
6245 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6246 ok(result == 12, "got %Id, expected %d\n", result, 12);
6247 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6248 ok (!strcmp(buffer, "line1"),
6249 "EM_STREAMIN: Unexpected text '%s'\n", buffer);
6251 /* Test 0-bytes inside text */
6252 hwndRichEdit = new_richedit_with_style(NULL, 0);
6253 phase = 0;
6254 es.dwCookie = (DWORD_PTR)&phase;
6255 es.dwError = 0;
6256 es.pfnCallback = test_EM_STREAMIN_null_bytes;
6257 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
6258 ok(result == 16, "got %Id, expected %d\n", result, 16);
6259 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6260 ok (!strcmp(buffer, "Th is is a test"), "EM_STREAMIN: Unexpected text '%s'\n", buffer);
6263 static void test_EM_StreamIn_Undo(void)
6265 /* The purpose of this test is to determine when a EM_StreamIn should be
6266 * undoable. This is important because WM_PASTE currently uses StreamIn and
6267 * pasting should always be undoable but streaming isn't always.
6269 * cases to test:
6270 * StreamIn plain text without SFF_SELECTION.
6271 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
6272 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
6273 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
6274 * Feel free to add tests for other text modes or StreamIn things.
6278 HWND hwndRichEdit = new_richedit(NULL);
6279 LRESULT result;
6280 EDITSTREAM es;
6281 char buffer[1024] = {0};
6282 const char randomtext[] = "Some text";
6284 es.pfnCallback = EditStreamCallback;
6286 /* StreamIn, no SFF_SELECTION */
6287 es.dwCookie = nCallbackCount;
6288 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6289 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6290 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
6291 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
6292 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6293 result = strcmp (buffer,"test");
6294 ok (result == 0,
6295 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
6297 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6298 ok (result == FALSE,
6299 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
6301 /* StreamIn, SFF_SELECTION, but nothing selected */
6302 es.dwCookie = nCallbackCount;
6303 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6304 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6305 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
6306 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
6307 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6308 result = strcmp (buffer,"testSome text");
6309 ok (result == 0,
6310 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6312 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6313 ok (result == TRUE,
6314 "EM_STREAMIN with SFF_SELECTION but no selection set "
6315 "should create an undo\n");
6317 /* StreamIn, SFF_SELECTION, with a selection */
6318 es.dwCookie = nCallbackCount;
6319 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
6320 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
6321 SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
6322 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
6323 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
6324 result = strcmp (buffer,"Sometesttext");
6325 ok (result == 0,
6326 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
6328 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
6329 ok (result == TRUE,
6330 "EM_STREAMIN with SFF_SELECTION and selection set "
6331 "should create an undo\n");
6333 DestroyWindow(hwndRichEdit);
6336 static BOOL is_em_settextex_supported(HWND hwnd)
6338 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
6339 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
6342 static void test_unicode_conversions(void)
6344 static const WCHAR tW[] = {'t',0};
6345 static const WCHAR teW[] = {'t','e',0};
6346 static const WCHAR textW[] = {'t','e','s','t',0};
6347 static const char textA[] = "test";
6348 char bufA[64];
6349 WCHAR bufW[64];
6350 HWND hwnd;
6351 int em_settextex_supported, ret;
6353 #define set_textA(hwnd, wm_set_text, txt) \
6354 do { \
6355 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
6356 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6357 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6358 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6359 ok(ret, "SendMessageA(%02x) error %lu\n", wm_set_text, GetLastError()); \
6360 } while(0)
6361 #define expect_textA(hwnd, wm_get_text, txt) \
6362 do { \
6363 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6364 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6365 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6366 memset(bufA, 0xAA, sizeof(bufA)); \
6367 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6368 ok(ret, "SendMessageA(%02x) error %lu\n", wm_get_text, GetLastError()); \
6369 ret = lstrcmpA(bufA, txt); \
6370 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
6371 } while(0)
6373 #define set_textW(hwnd, wm_set_text, txt) \
6374 do { \
6375 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
6376 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
6377 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
6378 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
6379 ok(ret, "SendMessageW(%02x) error %lu\n", wm_set_text, GetLastError()); \
6380 } while(0)
6381 #define expect_textW(hwnd, wm_get_text, txt) \
6382 do { \
6383 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
6384 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6385 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6386 memset(bufW, 0xAA, sizeof(bufW)); \
6387 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
6388 ok(ret, "SendMessageW(%02x) error %lu\n", wm_get_text, GetLastError()); \
6389 ret = lstrcmpW(bufW, txt); \
6390 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
6391 } while(0)
6392 #define expect_empty(hwnd, wm_get_text) \
6393 do { \
6394 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
6395 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
6396 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
6397 memset(bufA, 0xAA, sizeof(bufA)); \
6398 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
6399 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
6400 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
6401 } while(0)
6403 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6404 0, 0, 200, 60, 0, 0, 0, 0);
6405 ok(hwnd != 0, "CreateWindowExA error %lu\n", GetLastError());
6407 ret = IsWindowUnicode(hwnd);
6408 ok(ret, "RichEdit20W should be unicode under NT\n");
6410 /* EM_SETTEXTEX is supported starting from version 3.0 */
6411 em_settextex_supported = is_em_settextex_supported(hwnd);
6412 trace("EM_SETTEXTEX is %ssupported on this platform\n",
6413 em_settextex_supported ? "" : "NOT ");
6415 expect_empty(hwnd, WM_GETTEXT);
6416 expect_empty(hwnd, EM_GETTEXTEX);
6418 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
6419 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6420 expect_textA(hwnd, WM_GETTEXT, "t");
6421 expect_textA(hwnd, EM_GETTEXTEX, "t");
6422 expect_textW(hwnd, EM_GETTEXTEX, tW);
6424 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
6425 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
6426 expect_textA(hwnd, WM_GETTEXT, "te");
6427 expect_textA(hwnd, EM_GETTEXTEX, "te");
6428 expect_textW(hwnd, EM_GETTEXTEX, teW);
6430 set_textA(hwnd, WM_SETTEXT, NULL);
6431 expect_empty(hwnd, WM_GETTEXT);
6432 expect_empty(hwnd, EM_GETTEXTEX);
6434 set_textA(hwnd, WM_SETTEXT, textA);
6435 expect_textA(hwnd, WM_GETTEXT, textA);
6436 expect_textA(hwnd, EM_GETTEXTEX, textA);
6437 expect_textW(hwnd, EM_GETTEXTEX, textW);
6439 if (em_settextex_supported)
6441 set_textA(hwnd, EM_SETTEXTEX, textA);
6442 expect_textA(hwnd, WM_GETTEXT, textA);
6443 expect_textA(hwnd, EM_GETTEXTEX, textA);
6444 expect_textW(hwnd, EM_GETTEXTEX, textW);
6447 set_textW(hwnd, WM_SETTEXT, textW);
6448 expect_textW(hwnd, WM_GETTEXT, textW);
6449 expect_textA(hwnd, WM_GETTEXT, textA);
6450 expect_textW(hwnd, EM_GETTEXTEX, textW);
6451 expect_textA(hwnd, EM_GETTEXTEX, textA);
6453 if (em_settextex_supported)
6455 set_textW(hwnd, EM_SETTEXTEX, textW);
6456 expect_textW(hwnd, WM_GETTEXT, textW);
6457 expect_textA(hwnd, WM_GETTEXT, textA);
6458 expect_textW(hwnd, EM_GETTEXTEX, textW);
6459 expect_textA(hwnd, EM_GETTEXTEX, textA);
6461 DestroyWindow(hwnd);
6463 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
6464 0, 0, 200, 60, 0, 0, 0, 0);
6465 ok(hwnd != 0, "CreateWindowExA error %lu\n", GetLastError());
6467 ret = IsWindowUnicode(hwnd);
6468 ok(!ret, "RichEdit20A should NOT be unicode\n");
6470 set_textA(hwnd, WM_SETTEXT, textA);
6471 expect_textA(hwnd, WM_GETTEXT, textA);
6472 expect_textA(hwnd, EM_GETTEXTEX, textA);
6473 expect_textW(hwnd, EM_GETTEXTEX, textW);
6475 if (em_settextex_supported)
6477 set_textA(hwnd, EM_SETTEXTEX, textA);
6478 expect_textA(hwnd, WM_GETTEXT, textA);
6479 expect_textA(hwnd, EM_GETTEXTEX, textA);
6480 expect_textW(hwnd, EM_GETTEXTEX, textW);
6483 set_textW(hwnd, WM_SETTEXT, textW);
6484 expect_textW(hwnd, WM_GETTEXT, textW);
6485 expect_textA(hwnd, WM_GETTEXT, textA);
6486 expect_textW(hwnd, EM_GETTEXTEX, textW);
6487 expect_textA(hwnd, EM_GETTEXTEX, textA);
6489 if (em_settextex_supported)
6491 set_textW(hwnd, EM_SETTEXTEX, textW);
6492 expect_textW(hwnd, WM_GETTEXT, textW);
6493 expect_textA(hwnd, WM_GETTEXT, textA);
6494 expect_textW(hwnd, EM_GETTEXTEX, textW);
6495 expect_textA(hwnd, EM_GETTEXTEX, textA);
6497 DestroyWindow(hwnd);
6500 static void test_WM_CHAR(void)
6502 HWND hwnd;
6503 int ret;
6504 const char * char_list = "abc\rabc\r";
6505 const char * expected_content_single = "abcabc";
6506 const char * expected_content_multi = "abc\r\nabc\r\n";
6507 char buffer[64] = {0};
6508 const char * p;
6510 /* single-line control must IGNORE carriage returns */
6511 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6512 0, 0, 200, 60, 0, 0, 0, 0);
6513 ok(hwnd != 0, "CreateWindowExA error %lu\n", GetLastError());
6514 disable_beep( hwnd );
6516 p = char_list;
6517 while (*p != '\0') {
6518 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6519 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6520 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6521 SendMessageA(hwnd, WM_KEYUP, *p, 1);
6522 p++;
6525 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6526 ret = strcmp(buffer, expected_content_single);
6527 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6529 DestroyWindow(hwnd);
6531 /* multi-line control inserts CR normally */
6532 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6533 0, 0, 200, 60, 0, 0, 0, 0);
6534 ok(hwnd != 0, "CreateWindowExA error %lu\n", GetLastError());
6536 p = char_list;
6537 while (*p != '\0') {
6538 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6539 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6540 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6541 SendMessageA(hwnd, WM_KEYUP, *p, 1);
6542 p++;
6545 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6546 ret = strcmp(buffer, expected_content_multi);
6547 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6549 DestroyWindow(hwnd);
6552 static void test_EM_GETTEXTLENGTHEX(void)
6554 HWND hwnd;
6555 GETTEXTLENGTHEX gtl;
6556 int ret;
6557 const char * base_string = "base string";
6558 const char * test_string = "a\nb\n\n\r\n";
6559 const char * test_string_after = "a";
6560 const char * test_string_2 = "a\rtest\rstring";
6561 char buffer[64] = {0};
6563 /* single line */
6564 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6565 0, 0, 200, 60, 0, 0, 0, 0);
6566 ok(hwnd != 0, "CreateWindowExA error %lu\n", GetLastError());
6568 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6569 gtl.codepage = CP_ACP;
6570 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6571 ok(ret == 0, "ret %d\n",ret);
6573 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6574 gtl.codepage = CP_ACP;
6575 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6576 ok(ret == 0, "ret %d\n",ret);
6578 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6580 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6581 gtl.codepage = CP_ACP;
6582 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6583 ok(ret == strlen(base_string), "ret %d\n",ret);
6585 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6586 gtl.codepage = CP_ACP;
6587 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6588 ok(ret == strlen(base_string), "ret %d\n",ret);
6590 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6592 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6593 gtl.codepage = CP_ACP;
6594 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6595 ok(ret == 1, "ret %d\n",ret);
6597 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6598 gtl.codepage = CP_ACP;
6599 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6600 ok(ret == 1, "ret %d\n",ret);
6602 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6603 ret = strcmp(buffer, test_string_after);
6604 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6606 DestroyWindow(hwnd);
6608 /* multi line */
6609 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
6610 0, 0, 200, 60, 0, 0, 0, 0);
6611 ok(hwnd != 0, "CreateWindowExA error %lu\n", GetLastError());
6613 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6614 gtl.codepage = CP_ACP;
6615 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6616 ok(ret == 0, "ret %d\n",ret);
6618 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6619 gtl.codepage = CP_ACP;
6620 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6621 ok(ret == 0, "ret %d\n",ret);
6623 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6625 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6626 gtl.codepage = CP_ACP;
6627 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6628 ok(ret == strlen(base_string), "ret %d\n",ret);
6630 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6631 gtl.codepage = CP_ACP;
6632 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6633 ok(ret == strlen(base_string), "ret %d\n",ret);
6635 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6637 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6638 gtl.codepage = CP_ACP;
6639 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6640 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
6642 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6643 gtl.codepage = CP_ACP;
6644 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6645 ok(ret == strlen(test_string_2), "ret %d\n",ret);
6647 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6649 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6650 gtl.codepage = CP_ACP;
6651 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6652 ok(ret == 10, "ret %d\n",ret);
6654 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6655 gtl.codepage = CP_ACP;
6656 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6657 ok(ret == 6, "ret %d\n",ret);
6659 /* Unicode/NUMCHARS/NUMBYTES */
6660 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6662 gtl.flags = GTL_DEFAULT;
6663 gtl.codepage = 1200;
6664 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6665 ok(ret == lstrlenA(test_string_2),
6666 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6668 gtl.flags = GTL_NUMCHARS;
6669 gtl.codepage = 1200;
6670 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6671 ok(ret == lstrlenA(test_string_2),
6672 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6674 gtl.flags = GTL_NUMBYTES;
6675 gtl.codepage = 1200;
6676 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6677 ok(ret == lstrlenA(test_string_2)*2,
6678 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6680 gtl.flags = GTL_PRECISE;
6681 gtl.codepage = 1200;
6682 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6683 ok(ret == lstrlenA(test_string_2)*2,
6684 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6686 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6687 gtl.codepage = 1200;
6688 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6689 ok(ret == lstrlenA(test_string_2),
6690 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6692 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
6693 gtl.codepage = 1200;
6694 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6695 ok(ret == E_INVALIDARG,
6696 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %li\n", ret, E_INVALIDARG);
6698 DestroyWindow(hwnd);
6702 /* globals that parent and child access when checking event masks & notifications */
6703 static HWND eventMaskEditHwnd = 0;
6704 static int queriedEventMask;
6705 static int watchForEventMask = 0;
6707 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
6708 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6710 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
6712 queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6714 return DefWindowProcA(hwnd, message, wParam, lParam);
6717 /* test event masks in combination with WM_COMMAND */
6718 static void test_eventMask(void)
6720 HWND parent;
6721 int ret, style;
6722 WNDCLASSA cls;
6723 const char text[] = "foo bar\n";
6724 int eventMask;
6726 /* register class to capture WM_COMMAND */
6727 cls = make_simple_class(ParentMsgCheckProcA, "EventMaskParentClass");
6728 if(!RegisterClassA(&cls)) assert(0);
6730 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6731 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6732 ok (parent != 0, "Failed to create parent window\n");
6734 eventMaskEditHwnd = new_richedit(parent);
6735 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
6737 eventMask = ENM_CHANGE | ENM_UPDATE;
6738 ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
6739 ok(ret == ENM_NONE, "wrong event mask\n");
6740 ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6741 ok(ret == eventMask, "failed to set event mask\n");
6743 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
6744 queriedEventMask = 0; /* initialize to something other than we expect */
6745 watchForEventMask = EN_CHANGE;
6746 ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
6747 ok(ret == TRUE, "failed to set text\n");
6748 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
6749 notification in response to WM_SETTEXT */
6750 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6751 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6753 /* check to see if EN_CHANGE is sent when redraw is turned off */
6754 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6755 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6756 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
6757 /* redraw is disabled by making the window invisible. */
6758 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6759 queriedEventMask = 0; /* initialize to something other than we expect */
6760 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6761 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6762 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6763 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
6764 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6766 /* check to see if EN_UPDATE is sent when the editor isn't visible */
6767 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6768 style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
6769 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
6770 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6771 watchForEventMask = EN_UPDATE;
6772 queriedEventMask = 0; /* initialize to something other than we expect */
6773 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6774 ok(queriedEventMask == 0,
6775 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6776 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
6777 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6778 queriedEventMask = 0; /* initialize to something other than we expect */
6779 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6780 ok(queriedEventMask == eventMask,
6781 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6784 DestroyWindow(parent);
6787 static int received_WM_NOTIFY = 0;
6788 static int modify_at_WM_NOTIFY = 0;
6789 static BOOL filter_on_WM_NOTIFY = FALSE;
6790 static HWND hwndRichedit_WM_NOTIFY;
6792 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6794 if(message == WM_NOTIFY)
6796 received_WM_NOTIFY = 1;
6797 modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
6798 if (filter_on_WM_NOTIFY) return TRUE;
6800 return DefWindowProcA(hwnd, message, wParam, lParam);
6803 static void test_WM_NOTIFY(void)
6805 HWND parent;
6806 WNDCLASSA cls;
6807 CHARFORMAT2A cf2;
6808 int sel_start, sel_end;
6810 /* register class to capture WM_NOTIFY */
6811 cls = make_simple_class(WM_NOTIFY_ParentMsgCheckProcA, "WM_NOTIFY_ParentClass");
6812 if(!RegisterClassA(&cls)) assert(0);
6814 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6815 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6816 ok (parent != 0, "Failed to create parent window\n");
6818 hwndRichedit_WM_NOTIFY = new_richedit(parent);
6819 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
6821 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
6823 /* Notifications for selection change should only be sent when selection
6824 actually changes. EM_SETCHARFORMAT is one message that calls
6825 ME_CommitUndo, which should check whether message should be sent */
6826 received_WM_NOTIFY = 0;
6827 cf2.cbSize = sizeof(CHARFORMAT2A);
6828 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
6829 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
6830 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
6831 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6832 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6834 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
6835 already at 0. */
6836 received_WM_NOTIFY = 0;
6837 modify_at_WM_NOTIFY = 0;
6838 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6839 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6840 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6842 received_WM_NOTIFY = 0;
6843 modify_at_WM_NOTIFY = 0;
6844 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
6845 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6847 received_WM_NOTIFY = 0;
6848 modify_at_WM_NOTIFY = 0;
6849 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6850 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6851 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6853 /* Test for WM_NOTIFY messages with redraw disabled. */
6854 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6855 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
6856 received_WM_NOTIFY = 0;
6857 SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
6858 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6859 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
6861 /* Test filtering key events. */
6862 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6863 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
6864 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6865 received_WM_NOTIFY = 0;
6866 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6867 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6868 ok(sel_start == 1 && sel_end == 1,
6869 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6870 filter_on_WM_NOTIFY = TRUE;
6871 received_WM_NOTIFY = 0;
6872 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6873 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6874 ok(sel_start == 1 && sel_end == 1,
6875 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6877 /* test with owner set to NULL */
6878 SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
6879 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6880 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6881 ok(sel_start == 1 && sel_end == 1,
6882 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6884 DestroyWindow(hwndRichedit_WM_NOTIFY);
6885 DestroyWindow(parent);
6888 static ENLINK enlink;
6889 #define CURSOR_CLIENT_X 5
6890 #define CURSOR_CLIENT_Y 5
6891 #define WP_PARENT 1
6892 #define WP_CHILD 2
6894 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6896 if(message == WM_NOTIFY && ((NMHDR*)lParam)->code == EN_LINK)
6898 enlink = *(ENLINK*)lParam;
6900 return DefWindowProcA(hwnd, message, wParam, lParam);
6903 static void link_notify_test(const char *desc, int i, HWND hwnd, HWND parent,
6904 UINT msg, WPARAM wParam, LPARAM lParam, BOOL notifies)
6906 ENLINK junk_enlink;
6908 switch (msg)
6910 case WM_LBUTTONDBLCLK:
6911 case WM_LBUTTONDOWN:
6912 case WM_LBUTTONUP:
6913 case WM_MOUSEHOVER:
6914 case WM_MOUSEMOVE:
6915 case WM_MOUSEWHEEL:
6916 case WM_RBUTTONDBLCLK:
6917 case WM_RBUTTONDOWN:
6918 case WM_RBUTTONUP:
6919 lParam = MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y);
6920 break;
6921 case WM_SETCURSOR:
6922 if (wParam == WP_PARENT)
6923 wParam = (WPARAM)parent;
6924 else if (wParam == WP_CHILD)
6925 wParam = (WPARAM)hwnd;
6926 break;
6929 memset(&junk_enlink, 0x23, sizeof(junk_enlink));
6930 enlink = junk_enlink;
6932 SendMessageA(hwnd, msg, wParam, lParam);
6934 if (notifies)
6936 ok(enlink.nmhdr.hwndFrom == hwnd,
6937 "%s test %i: Expected hwnd %p got %p\n", desc, i, hwnd, enlink.nmhdr.hwndFrom);
6938 ok(enlink.nmhdr.idFrom == 0,
6939 "%s test %i: Expected idFrom 0 got 0x%Ix\n", desc, i, enlink.nmhdr.idFrom);
6940 ok(enlink.msg == msg,
6941 "%s test %i: Expected msg 0x%x got 0x%x\n", desc, i, msg, enlink.msg);
6942 if (msg == WM_SETCURSOR)
6944 ok(enlink.wParam == 0,
6945 "%s test %i: Expected wParam 0 got 0x%Ix\n", desc, i, enlink.wParam);
6947 else
6949 ok(enlink.wParam == wParam,
6950 "%s test %i: Expected wParam 0x%Ix got 0x%Ix\n", desc, i, wParam, enlink.wParam);
6952 ok(enlink.lParam == MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y),
6953 "%s test %i: Expected lParam 0x%Ix got 0x%Ix\n",
6954 desc, i, MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y), enlink.lParam);
6955 ok(enlink.chrg.cpMin == 0 && enlink.chrg.cpMax == 31,
6956 "%s test %i: Expected link range [0,31) got [%li,%li)\n", desc, i, enlink.chrg.cpMin, enlink.chrg.cpMax);
6958 else
6960 ok(memcmp(&enlink, &junk_enlink, sizeof(enlink)) == 0,
6961 "%s test %i: Expected enlink to remain unmodified\n", desc, i);
6965 static void test_EN_LINK(void)
6967 HWND hwnd, parent;
6968 WNDCLASSA cls;
6969 CHARFORMAT2A cf2;
6970 POINT orig_cursor_pos;
6971 POINT cursor_screen_pos = {CURSOR_CLIENT_X, CURSOR_CLIENT_Y};
6972 int i;
6974 static const struct
6976 UINT msg;
6977 WPARAM wParam;
6978 LPARAM lParam;
6979 BOOL notifies;
6981 link_notify_tests[] =
6983 /* hold down the left button and try some messages */
6984 { WM_LBUTTONDOWN, 0, 0, TRUE }, /* 0 */
6985 { EM_LINESCROLL, 0, 1, FALSE },
6986 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6987 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6988 { WM_MOUSEHOVER, 0, 0, FALSE },
6989 { WM_MOUSEMOVE, 0, 0, FALSE },
6990 { WM_MOUSEWHEEL, 0, 0, FALSE },
6991 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6992 { WM_RBUTTONDOWN, 0, 0, TRUE },
6993 { WM_RBUTTONUP, 0, 0, TRUE },
6994 { WM_SETCURSOR, 0, 0, FALSE },
6995 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6996 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6997 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6998 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6999 { WM_LBUTTONUP, 0, 0, TRUE },
7000 /* hold down the right button and try some messages */
7001 { WM_RBUTTONDOWN, 0, 0, TRUE }, /* 16 */
7002 { EM_LINESCROLL, 0, 1, FALSE },
7003 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
7004 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
7005 { WM_LBUTTONDOWN, 0, 0, TRUE },
7006 { WM_LBUTTONUP, 0, 0, TRUE },
7007 { WM_MOUSEHOVER, 0, 0, FALSE },
7008 { WM_MOUSEMOVE, 0, 0, TRUE },
7009 { WM_MOUSEWHEEL, 0, 0, FALSE },
7010 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
7011 { WM_SETCURSOR, 0, 0, FALSE },
7012 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
7013 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
7014 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
7015 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
7016 { WM_RBUTTONUP, 0, 0, TRUE },
7017 /* try the messages with both buttons released */
7018 { EM_LINESCROLL, 0, 1, FALSE }, /* 32 */
7019 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
7020 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
7021 { WM_LBUTTONDOWN, 0, 0, TRUE },
7022 { WM_LBUTTONUP, 0, 0, TRUE },
7023 { WM_MOUSEHOVER, 0, 0, FALSE },
7024 { WM_MOUSEMOVE, 0, 0, TRUE },
7025 { WM_MOUSEWHEEL, 0, 0, FALSE },
7026 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
7027 { WM_RBUTTONDOWN, 0, 0, TRUE },
7028 { WM_RBUTTONUP, 0, 0, TRUE },
7029 { WM_SETCURSOR, 0, 0, FALSE },
7030 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
7031 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
7032 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
7033 { WM_VSCROLL, SB_BOTTOM, 0, FALSE }
7036 /* register class to capture WM_NOTIFY */
7037 cls = make_simple_class(EN_LINK_ParentMsgCheckProcA, "EN_LINK_ParentClass");
7038 if(!RegisterClassA(&cls)) assert(0);
7040 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
7041 0, 0, 200, 60, NULL, NULL, NULL, NULL);
7042 ok(parent != 0, "Failed to create parent window\n");
7044 hwnd = new_richedit(parent);
7045 ok(hwnd != 0, "Failed to create edit window\n");
7047 SendMessageA(hwnd, EM_SETEVENTMASK, 0, ENM_LINK);
7049 cf2.cbSize = sizeof(CHARFORMAT2A);
7050 cf2.dwMask = CFM_LINK;
7051 cf2.dwEffects = CFE_LINK;
7052 SendMessageA(hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
7053 /* mixing letters and numbers causes runs to be split */
7054 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
7056 GetCursorPos(&orig_cursor_pos);
7057 SetCursorPos(0, 0);
7059 for (i = 0; i < ARRAY_SIZE(link_notify_tests); i++)
7061 link_notify_test("cursor position simulated", i, hwnd, parent,
7062 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
7063 link_notify_tests[i].msg == WM_SETCURSOR ? FALSE : link_notify_tests[i].notifies);
7066 ClientToScreen(hwnd, &cursor_screen_pos);
7067 SetCursorPos(cursor_screen_pos.x, cursor_screen_pos.y);
7069 for (i = 0; i < ARRAY_SIZE(link_notify_tests); i++)
7071 link_notify_test("cursor position set", i, hwnd, parent,
7072 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
7073 link_notify_tests[i].notifies);
7076 SetCursorPos(orig_cursor_pos.x, orig_cursor_pos.y);
7077 DestroyWindow(hwnd);
7078 DestroyWindow(parent);
7081 static void test_undo_coalescing(void)
7083 HWND hwnd;
7084 int result;
7085 char buffer[64] = {0};
7087 /* multi-line control inserts CR normally */
7088 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
7089 0, 0, 200, 60, 0, 0, 0, 0);
7090 ok(hwnd != 0, "CreateWindowExA error %lu\n", GetLastError());
7091 disable_beep( hwnd );
7093 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
7094 ok (result == FALSE, "Can undo after window creation.\n");
7095 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7096 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
7097 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
7098 ok (result == FALSE, "Can redo after window creation.\n");
7099 result = SendMessageA(hwnd, EM_REDO, 0, 0);
7100 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
7102 /* Test the effect of arrows keys during typing on undo transactions*/
7103 simulate_typing_characters(hwnd, "one two three");
7104 SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
7105 SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
7106 simulate_typing_characters(hwnd, " four five six");
7108 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
7109 ok (result == FALSE, "Can redo before anything is undone.\n");
7110 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
7111 ok (result == TRUE, "Cannot undo typed characters.\n");
7112 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7113 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
7114 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
7115 ok (result == TRUE, "Cannot redo after undo.\n");
7116 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7117 result = strcmp(buffer, "one two three");
7118 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
7120 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
7121 ok (result == TRUE, "Cannot undo typed characters.\n");
7122 result = SendMessageA(hwnd, WM_UNDO, 0, 0);
7123 ok (result == TRUE, "Failed to undo typed characters.\n");
7124 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7125 result = strcmp(buffer, "");
7126 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
7128 /* Test the effect of focus changes during typing on undo transactions*/
7129 simulate_typing_characters(hwnd, "one two three");
7130 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
7131 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
7132 SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
7133 SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
7134 simulate_typing_characters(hwnd, " four five six");
7135 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7136 ok (result == TRUE, "Failed to undo typed characters.\n");
7137 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7138 result = strcmp(buffer, "one two three");
7139 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
7141 /* Test the effect of the back key during typing on undo transactions */
7142 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
7143 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
7144 ok (result == TRUE, "Failed to clear the text.\n");
7145 simulate_typing_characters(hwnd, "one two threa");
7146 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
7147 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
7148 SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
7149 SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
7150 simulate_typing_characters(hwnd, "e four five six");
7151 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7152 ok (result == TRUE, "Failed to undo typed characters.\n");
7153 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7154 result = strcmp(buffer, "");
7155 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
7157 /* Test the effect of the delete key during typing on undo transactions */
7158 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
7159 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
7160 ok(result == TRUE, "Failed to set the text.\n");
7161 SendMessageA(hwnd, EM_SETSEL, 1, 1);
7162 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
7163 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
7164 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
7165 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
7166 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7167 ok (result == TRUE, "Failed to undo typed characters.\n");
7168 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7169 result = strcmp(buffer, "acd");
7170 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
7171 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7172 ok (result == TRUE, "Failed to undo typed characters.\n");
7173 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7174 result = strcmp(buffer, "abcd");
7175 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
7177 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
7178 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
7179 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
7180 ok (result == TRUE, "Failed to clear the text.\n");
7181 simulate_typing_characters(hwnd, "one two three");
7182 result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
7183 ok (result == 0, "expected %d but got %d\n", 0, result);
7184 simulate_typing_characters(hwnd, " four five six");
7185 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7186 ok (result == TRUE, "Failed to undo typed characters.\n");
7187 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7188 result = strcmp(buffer, "one two three");
7189 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
7190 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
7191 ok (result == TRUE, "Failed to undo typed characters.\n");
7192 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
7193 result = strcmp(buffer, "");
7194 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
7196 DestroyWindow(hwnd);
7199 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
7201 int length;
7203 /* MSDN lied, length is actually the number of bytes. */
7204 length = bytes / sizeof(WCHAR);
7205 switch(code)
7207 case WB_ISDELIMITER:
7208 return text[pos] == 'X';
7209 case WB_LEFT:
7210 case WB_MOVEWORDLEFT:
7211 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7212 return pos-1;
7213 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
7214 case WB_LEFTBREAK:
7215 pos--;
7216 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7217 pos--;
7218 return pos;
7219 case WB_RIGHT:
7220 case WB_MOVEWORDRIGHT:
7221 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7222 return pos+1;
7223 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
7224 case WB_RIGHTBREAK:
7225 pos++;
7226 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
7227 pos++;
7228 return pos;
7229 default:
7230 ok(FALSE, "Unexpected code %d\n", code);
7231 break;
7233 return 0;
7236 static void test_word_movement(void)
7238 HWND hwnd;
7239 int result;
7240 int sel_start, sel_end;
7241 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
7243 /* multi-line control inserts CR normally */
7244 hwnd = new_richedit(NULL);
7246 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
7247 ok (result == TRUE, "Failed to clear the text.\n");
7248 SendMessageA(hwnd, EM_SETSEL, 0, 0);
7249 /* |one two three */
7251 send_ctrl_key(hwnd, VK_RIGHT);
7252 /* one |two three */
7253 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7254 ok(sel_start == sel_end, "Selection should be empty\n");
7255 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
7257 send_ctrl_key(hwnd, VK_RIGHT);
7258 /* one two |three */
7259 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7260 ok(sel_start == sel_end, "Selection should be empty\n");
7261 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7263 send_ctrl_key(hwnd, VK_LEFT);
7264 /* one |two three */
7265 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7266 ok(sel_start == sel_end, "Selection should be empty\n");
7267 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
7269 send_ctrl_key(hwnd, VK_LEFT);
7270 /* |one two three */
7271 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7272 ok(sel_start == sel_end, "Selection should be empty\n");
7273 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
7275 SendMessageA(hwnd, EM_SETSEL, 8, 8);
7276 /* one two | three */
7277 send_ctrl_key(hwnd, VK_RIGHT);
7278 /* one two |three */
7279 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7280 ok(sel_start == sel_end, "Selection should be empty\n");
7281 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7283 SendMessageA(hwnd, EM_SETSEL, 11, 11);
7284 /* one two th|ree */
7285 send_ctrl_key(hwnd, VK_LEFT);
7286 /* one two |three */
7287 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7288 ok(sel_start == sel_end, "Selection should be empty\n");
7289 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
7291 /* Test with a custom word break procedure that uses X as the delimiter. */
7292 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
7293 ok (result == TRUE, "Failed to clear the text.\n");
7294 SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7295 /* |one twoXthree */
7296 send_ctrl_key(hwnd, VK_RIGHT);
7297 /* one twoX|three */
7298 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7299 ok(sel_start == sel_end, "Selection should be empty\n");
7300 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7302 DestroyWindow(hwnd);
7304 /* Make sure the behaviour is the same with a unicode richedit window,
7305 * and using unicode functions. */
7307 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
7308 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7309 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7311 /* Test with a custom word break procedure that uses X as the delimiter. */
7312 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
7313 ok (result == TRUE, "Failed to clear the text.\n");
7314 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
7315 /* |one twoXthree */
7316 send_ctrl_key(hwnd, VK_RIGHT);
7317 /* one twoX|three */
7318 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7319 ok(sel_start == sel_end, "Selection should be empty\n");
7320 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
7322 DestroyWindow(hwnd);
7325 static void test_word_movement_multiline(void)
7327 DWORD sel_start, sel_end;
7328 LRESULT result;
7329 HWND hwnd;
7331 /* multi-line control inserts CR normally */
7332 hwnd = new_richedit(NULL);
7334 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"Lorem ipsum\rdolor sit\rnamet");
7335 ok(result == TRUE, "WM_SETTEXT returned %Iu.\n", result);
7336 SendMessageA(hwnd, EM_SETSEL, 0, 0);
7337 /* [|Lorem ipsum] [dolor sit] [amet] */
7339 send_ctrl_key(hwnd, VK_RIGHT);
7340 /* [Lorem |ipsum] [dolor sit] [amet] */
7341 sel_start = sel_end = 0xdeadbeefUL;
7342 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7343 ok(sel_start == sel_end, "expected sel length to be 0, got %lu.\n", sel_end - sel_start);
7344 ok(sel_start == 6, "expected sel_start to be %u, got %lu.\n", 6, sel_start);
7346 send_ctrl_key(hwnd, VK_RIGHT);
7347 /* [Lorem ipsum|] [dolor sit] [amet] */
7348 sel_start = sel_end = 0xdeadbeefUL;
7349 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7350 ok(sel_start == sel_end, "expected sel length to be 0, got %lu.\n", sel_end - sel_start);
7351 ok(sel_start == 11, "expected sel_start to be %u, got %lu.\n", 11, sel_start);
7353 send_ctrl_key(hwnd, VK_RIGHT);
7354 /* [Lorem ipsum] [|dolor sit] [amet] */
7355 sel_start = sel_end = 0xdeadbeefUL;
7356 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7357 ok(sel_start == sel_end, "expected sel length to be 0, got %lu.\n", sel_end - sel_start);
7358 ok(sel_start == 12, "expected sel_start to be %u, got %lu.\n", 12, sel_start);
7360 send_ctrl_key(hwnd, VK_LEFT);
7361 /* [Lorem ipsum|] [dolor sit] [amet] */
7362 sel_start = sel_end = 0xdeadbeefUL;
7363 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
7364 ok(sel_start == sel_end, "expected sel length to be 0, got %lu.\n", sel_end - sel_start);
7365 ok(sel_start == 11, "expected sel_start to be %u, got %lu.\n", 11, sel_start);
7367 DestroyWindow(hwnd);
7370 static void test_EM_CHARFROMPOS(void)
7372 HWND hwnd;
7373 int result;
7374 RECT rcClient;
7375 POINTL point;
7376 point.x = 0;
7377 point.y = 40;
7379 /* multi-line control inserts CR normally */
7380 hwnd = new_richedit(NULL);
7381 result = SendMessageA(hwnd, WM_SETTEXT, 0,
7382 (LPARAM)"one two three four five six seven\reight");
7383 ok(result == 1, "Expected 1, got %d\n", result);
7384 GetClientRect(hwnd, &rcClient);
7386 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7387 ok(result == 34, "expected character index of 34 but got %d\n", result);
7389 /* Test with points outside the bounds of the richedit control. */
7390 point.x = -1;
7391 point.y = 40;
7392 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7393 ok(result == 34, "expected character index of 34 but got %d\n", result);
7395 point.x = 1000;
7396 point.y = 0;
7397 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7398 ok(result == 33, "expected character index of 33 but got %d\n", result);
7400 point.x = 1000;
7401 point.y = 36;
7402 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7403 ok(result == 39, "expected character index of 39 but got %d\n", result);
7405 point.x = 1000;
7406 point.y = -1;
7407 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7408 /* This differs from the msftedit result */
7409 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
7411 point.x = 1000;
7412 point.y = rcClient.bottom + 1;
7413 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7414 /* This differs from the msftedit result */
7415 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
7417 point.x = 1000;
7418 point.y = rcClient.bottom;
7419 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7420 ok(result == 39, "expected character index of 39 but got %d\n", result);
7422 DestroyWindow(hwnd);
7425 static void test_word_wrap(void)
7427 HWND hwnd;
7428 POINTL point = {0, 60}; /* This point must be below the first line */
7429 const char *text = "Must be long enough to test line wrapping";
7430 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
7431 int res, pos, lines;
7433 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
7434 * when specified on window creation and set later. */
7435 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7436 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7437 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7438 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7439 ok(res, "WM_SETTEXT failed.\n");
7440 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7441 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7442 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7443 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7445 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
7446 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7447 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7448 DestroyWindow(hwnd);
7450 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
7451 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7452 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7454 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7455 ok(res, "WM_SETTEXT failed.\n");
7456 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7457 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7458 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7459 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7461 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7462 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7463 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7464 DestroyWindow(hwnd);
7466 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
7467 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7468 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7469 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7470 ok(res, "WM_SETTEXT failed.\n");
7471 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7472 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7474 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7475 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7476 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7477 DestroyWindow(hwnd);
7479 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
7480 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
7481 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
7482 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7483 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
7484 ok(res, "WM_SETTEXT failed.\n");
7485 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7486 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7488 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
7489 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7490 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7492 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
7493 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
7494 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7495 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7496 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
7498 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
7499 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
7500 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
7501 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
7502 DestroyWindow(hwnd);
7504 /* Test to see if wrapping happens with redraw disabled. */
7505 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
7506 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
7507 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
7508 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
7509 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
7510 ok(res, "EM_REPLACESEL failed.\n");
7511 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7512 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
7513 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
7514 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7515 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
7517 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7518 DestroyWindow(hwnd);
7521 static void test_autoscroll(void)
7523 HWND hwnd = new_richedit(NULL);
7524 int lines, ret, redraw;
7525 POINT pt;
7527 for (redraw = 0; redraw <= 1; redraw++) {
7528 trace("testing with WM_SETREDRAW=%d\n", redraw);
7529 SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
7530 SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
7531 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7532 ok(lines == 8, "%d lines instead of 8\n", lines);
7533 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7534 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7535 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
7536 ret = GetWindowLongA(hwnd, GWL_STYLE);
7537 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
7539 SendMessageA(hwnd, WM_SETTEXT, 0, 0);
7540 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
7541 ok(lines == 1, "%d lines instead of 1\n", lines);
7542 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
7543 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
7544 ok(pt.y == 0, "y scroll position is %ld after clearing text.\n", pt.y);
7545 ret = GetWindowLongA(hwnd, GWL_STYLE);
7546 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
7549 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7550 DestroyWindow(hwnd);
7552 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
7553 * auto vertical/horizontal scrolling options. */
7554 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7555 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
7556 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7557 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7558 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7559 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
7560 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
7561 ret = GetWindowLongA(hwnd, GWL_STYLE);
7562 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7563 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7564 DestroyWindow(hwnd);
7566 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7567 WS_POPUP|ES_MULTILINE,
7568 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7569 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7570 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7571 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
7572 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
7573 ret = GetWindowLongA(hwnd, GWL_STYLE);
7574 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7575 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7576 DestroyWindow(hwnd);
7580 static void test_format_rect(void)
7582 HWND hwnd;
7583 RECT rc, expected, clientRect;
7584 int n;
7585 DWORD options;
7587 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7588 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7589 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7590 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7592 GetClientRect(hwnd, &clientRect);
7594 expected = clientRect;
7595 InflateRect(&expected, -1, 0);
7596 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7597 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7598 wine_dbgstr_rect(&expected));
7600 for (n = -3; n <= 3; n++)
7602 rc = clientRect;
7603 InflateRect(&rc, -n, -n);
7604 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7606 expected = rc;
7607 expected.top = max(0, rc.top);
7608 expected.left = max(0, rc.left);
7609 expected.bottom = min(clientRect.bottom, rc.bottom);
7610 expected.right = min(clientRect.right, rc.right);
7611 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7612 ok(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, wine_dbgstr_rect(&rc),
7613 wine_dbgstr_rect(&expected));
7616 rc = clientRect;
7617 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7618 expected = clientRect;
7619 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7620 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7621 wine_dbgstr_rect(&expected));
7623 /* Adding the selectionbar adds the selectionbar width to the left side. */
7624 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
7625 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7626 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
7627 expected.left += 8; /* selection bar width */
7628 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7629 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7630 wine_dbgstr_rect(&expected));
7632 rc = clientRect;
7633 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7634 expected = clientRect;
7635 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7636 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7637 wine_dbgstr_rect(&expected));
7639 /* Removing the selectionbar subtracts the selectionbar width from the left side,
7640 * even if the left side is already 0. */
7641 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
7642 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7643 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
7644 expected.left -= 8; /* selection bar width */
7645 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7646 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7647 wine_dbgstr_rect(&expected));
7649 /* reset back to client rect and now try adding selection bar */
7650 SendMessageA(hwnd, EM_SETRECT, 0, 0);
7651 expected = clientRect;
7652 InflateRect(&expected, -1, 0);
7653 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7654 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7655 wine_dbgstr_rect(&expected));
7656 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
7657 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7658 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7659 wine_dbgstr_rect(&expected));
7660 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
7662 /* Set the absolute value of the formatting rectangle. */
7663 rc = clientRect;
7664 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7665 expected = clientRect;
7666 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7667 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7668 wine_dbgstr_rect(&expected));
7670 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
7671 * LPARAM as being a relative offset when the WPARAM value is 1, but these
7672 * tests show that this isn't true. */
7673 rc.top = 15;
7674 rc.left = 15;
7675 rc.bottom = clientRect.bottom - 15;
7676 rc.right = clientRect.right - 15;
7677 expected = rc;
7678 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7679 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7680 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7681 wine_dbgstr_rect(&expected));
7683 /* For some reason it does not limit the values to the client rect with
7684 * a WPARAM value of 1. */
7685 rc.top = -15;
7686 rc.left = -15;
7687 rc.bottom = clientRect.bottom + 15;
7688 rc.right = clientRect.right + 15;
7689 expected = rc;
7690 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7691 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7692 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7693 wine_dbgstr_rect(&expected));
7695 /* Reset to default rect and check how the format rect adjusts to window
7696 * resize and how it copes with very small windows */
7697 SendMessageA(hwnd, EM_SETRECT, 0, 0);
7699 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
7700 GetClientRect(hwnd, &clientRect);
7702 expected = clientRect;
7703 InflateRect(&expected, -1, 0);
7704 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7705 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7706 wine_dbgstr_rect(&expected));
7708 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
7709 GetClientRect(hwnd, &clientRect);
7711 expected = clientRect;
7712 InflateRect(&expected, -1, 0);
7713 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7714 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7715 wine_dbgstr_rect(&expected));
7717 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
7718 GetClientRect(hwnd, &clientRect);
7720 expected = clientRect;
7721 InflateRect(&expected, -1, 0);
7722 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7723 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7724 wine_dbgstr_rect(&expected));
7726 DestroyWindow(hwnd);
7728 /* The extended window style affects the formatting rectangle. */
7729 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
7730 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7731 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7732 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7734 GetClientRect(hwnd, &clientRect);
7736 expected = clientRect;
7737 expected.top += 1;
7738 InflateRect(&expected, -1, 0);
7739 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7740 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7741 wine_dbgstr_rect(&expected));
7743 rc = clientRect;
7744 InflateRect(&rc, -5, -5);
7745 expected = rc;
7746 expected.top -= 1;
7747 InflateRect(&expected, 1, 0);
7748 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7749 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7750 ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
7751 wine_dbgstr_rect(&expected));
7753 DestroyWindow(hwnd);
7756 static void test_WM_GETDLGCODE(void)
7758 HWND hwnd;
7759 UINT res, expected;
7760 MSG msg;
7762 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7764 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7765 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7766 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7767 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7768 msg.hwnd = hwnd;
7769 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
7770 expected = expected | DLGC_WANTMESSAGE;
7771 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7772 res, expected);
7773 DestroyWindow(hwnd);
7775 msg.message = WM_KEYDOWN;
7776 msg.wParam = VK_RETURN;
7777 msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7778 msg.pt.x = 0;
7779 msg.pt.y = 0;
7780 msg.time = GetTickCount();
7782 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7783 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7784 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7785 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7786 msg.hwnd = hwnd;
7787 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7788 expected = expected | DLGC_WANTMESSAGE;
7789 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7790 res, expected);
7791 DestroyWindow(hwnd);
7793 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7794 ES_MULTILINE|WS_POPUP,
7795 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7796 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7797 msg.hwnd = hwnd;
7798 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7799 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7800 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7801 res, expected);
7802 DestroyWindow(hwnd);
7804 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7805 ES_WANTRETURN|WS_POPUP,
7806 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7807 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7808 msg.hwnd = hwnd;
7809 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7810 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7811 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7812 res, expected);
7813 DestroyWindow(hwnd);
7815 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7816 WS_POPUP,
7817 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7818 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7819 msg.hwnd = hwnd;
7820 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7821 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7822 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7823 res, expected);
7824 DestroyWindow(hwnd);
7826 msg.wParam = VK_TAB;
7827 msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7829 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7830 ES_MULTILINE|WS_POPUP,
7831 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7832 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7833 msg.hwnd = hwnd;
7834 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7835 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7836 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7837 res, expected);
7838 DestroyWindow(hwnd);
7840 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7841 WS_POPUP,
7842 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7843 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7844 msg.hwnd = hwnd;
7845 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7846 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7847 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7848 res, expected);
7849 DestroyWindow(hwnd);
7851 hold_key(VK_CONTROL);
7853 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7854 ES_MULTILINE|WS_POPUP,
7855 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7856 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7857 msg.hwnd = hwnd;
7858 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7859 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7860 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7861 res, expected);
7862 DestroyWindow(hwnd);
7864 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7865 WS_POPUP,
7866 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7867 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7868 msg.hwnd = hwnd;
7869 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7870 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7871 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7872 res, expected);
7873 DestroyWindow(hwnd);
7875 release_key(VK_CONTROL);
7877 msg.wParam = 'a';
7878 msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
7880 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7881 ES_MULTILINE|WS_POPUP,
7882 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7883 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7884 msg.hwnd = hwnd;
7885 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7886 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7887 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7888 res, expected);
7889 DestroyWindow(hwnd);
7891 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7892 WS_POPUP,
7893 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7894 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7895 msg.hwnd = hwnd;
7896 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7897 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7898 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7899 res, expected);
7900 DestroyWindow(hwnd);
7902 msg.message = WM_CHAR;
7904 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7905 ES_MULTILINE|WS_POPUP,
7906 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7907 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7908 msg.hwnd = hwnd;
7909 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7910 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7911 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7912 res, expected);
7913 DestroyWindow(hwnd);
7915 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7916 WS_POPUP,
7917 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7918 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7919 msg.hwnd = hwnd;
7920 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7921 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7922 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7923 res, expected);
7924 DestroyWindow(hwnd);
7926 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7927 WS_POPUP|ES_SAVESEL,
7928 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7929 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7930 res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
7931 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
7932 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7933 res, expected);
7934 DestroyWindow(hwnd);
7937 static void test_zoom(void)
7939 HWND hwnd;
7940 UINT ret;
7941 RECT rc;
7942 POINT pt;
7943 int numerator, denominator;
7945 hwnd = new_richedit(NULL);
7946 GetClientRect(hwnd, &rc);
7947 pt.x = (rc.right - rc.left) / 2;
7948 pt.y = (rc.bottom - rc.top) / 2;
7949 ClientToScreen(hwnd, &pt);
7951 /* Test initial zoom value */
7952 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7953 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
7954 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
7955 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7957 /* test scroll wheel */
7958 hold_key(VK_CONTROL);
7959 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7960 MAKELPARAM(pt.x, pt.y));
7961 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7962 release_key(VK_CONTROL);
7964 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7965 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
7966 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7967 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7969 /* Test how much the mouse wheel can zoom in and out. */
7970 ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
7971 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7973 hold_key(VK_CONTROL);
7974 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7975 MAKELPARAM(pt.x, pt.y));
7976 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7977 release_key(VK_CONTROL);
7979 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7980 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
7981 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7982 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7984 ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
7985 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7987 hold_key(VK_CONTROL);
7988 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7989 MAKELPARAM(pt.x, pt.y));
7990 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7991 release_key(VK_CONTROL);
7993 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7994 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
7995 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7996 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7998 ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
7999 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
8001 hold_key(VK_CONTROL);
8002 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
8003 MAKELPARAM(pt.x, pt.y));
8004 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
8005 release_key(VK_CONTROL);
8007 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
8008 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
8009 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
8010 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
8012 ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
8013 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
8015 hold_key(VK_CONTROL);
8016 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
8017 MAKELPARAM(pt.x, pt.y));
8018 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
8019 release_key(VK_CONTROL);
8021 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
8022 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
8023 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
8024 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
8026 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
8027 ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
8028 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
8030 hold_key(VK_CONTROL);
8031 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
8032 MAKELPARAM(pt.x, pt.y));
8033 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
8034 release_key(VK_CONTROL);
8036 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
8037 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
8038 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
8039 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
8041 /* Test bounds checking on EM_SETZOOM */
8042 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
8043 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
8045 ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
8046 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
8048 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
8049 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
8051 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
8052 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
8053 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
8054 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
8056 ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
8057 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
8059 /* See if negative numbers are accepted. */
8060 ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
8061 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
8063 /* See if negative numbers are accepted. */
8064 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
8065 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
8067 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
8068 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
8069 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
8070 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
8072 /* Reset the zoom value */
8073 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
8074 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
8076 DestroyWindow(hwnd);
8079 struct dialog_mode_messages
8081 int wm_getdefid, wm_close, wm_nextdlgctl;
8084 static struct dialog_mode_messages dm_messages;
8086 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
8087 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
8088 "got %d\n", wmclose, dm_messages.wm_close); \
8089 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
8090 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
8091 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
8092 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
8094 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
8096 switch (iMsg)
8098 case DM_GETDEFID:
8099 dm_messages.wm_getdefid++;
8100 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
8101 case WM_NEXTDLGCTL:
8102 dm_messages.wm_nextdlgctl++;
8103 break;
8104 case WM_CLOSE:
8105 dm_messages.wm_close++;
8106 break;
8109 return DefWindowProcA(hwnd, iMsg, wParam, lParam);
8112 static void test_dialogmode(void)
8114 HWND hwRichEdit, hwParent, hwButton;
8115 MSG msg= {0};
8116 int lcount, r;
8117 WNDCLASSA cls;
8119 cls = make_simple_class(dialog_mode_wnd_proc, "DialogModeParentClass");
8120 if(!RegisterClassA(&cls)) assert(0);
8122 hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
8123 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
8125 /* Test richedit(ES_MULTILINE) */
8127 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
8129 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8130 ok(0 == r, "expected 0, got %d\n", r);
8131 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8132 ok(2 == lcount, "expected 2, got %d\n", lcount);
8134 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
8135 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8137 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8138 ok(0 == r, "expected 0, got %d\n", r);
8139 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8140 ok(3 == lcount, "expected 3, got %d\n", lcount);
8142 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8143 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8144 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8145 ok(0 == r, "expected 0, got %d\n", r);
8146 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8147 ok(3 == lcount, "expected 3, got %d\n", lcount);
8149 DestroyWindow(hwRichEdit);
8151 /* Test standalone richedit(ES_MULTILINE) */
8153 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
8155 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8156 ok(0 == r, "expected 0, got %d\n", r);
8157 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8158 ok(2 == lcount, "expected 2, got %d\n", lcount);
8160 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8161 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8163 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8164 ok(0 == r, "expected 0, got %d\n", r);
8165 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8166 ok(2 == lcount, "expected 2, got %d\n", lcount);
8168 DestroyWindow(hwRichEdit);
8170 /* Check a destination for messages */
8172 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
8174 SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
8175 SetParent( hwRichEdit, NULL);
8177 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8178 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8180 memset(&dm_messages, 0, sizeof(dm_messages));
8181 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8182 ok(0 == r, "expected 0, got %d\n", r);
8183 test_dm_messages(0, 1, 0);
8185 memset(&dm_messages, 0, sizeof(dm_messages));
8186 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8187 ok(0 == r, "expected 0, got %d\n", r);
8188 test_dm_messages(0, 0, 1);
8190 DestroyWindow(hwRichEdit);
8192 /* Check messages from richedit(ES_MULTILINE) */
8194 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
8196 memset(&dm_messages, 0, sizeof(dm_messages));
8197 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8198 ok(0 == r, "expected 0, got %d\n", r);
8199 test_dm_messages(0, 0, 0);
8201 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8202 ok(2 == lcount, "expected 2, got %d\n", lcount);
8204 memset(&dm_messages, 0, sizeof(dm_messages));
8205 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8206 ok(0 == r, "expected 0, got %d\n", r);
8207 test_dm_messages(0, 0, 0);
8209 memset(&dm_messages, 0, sizeof(dm_messages));
8210 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8211 ok(0 == r, "expected 0, got %d\n", r);
8212 test_dm_messages(0, 0, 0);
8214 memset(&dm_messages, 0, sizeof(dm_messages));
8215 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8216 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8217 test_dm_messages(0, 0, 0);
8219 memset(&dm_messages, 0, sizeof(dm_messages));
8220 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8221 ok(0 == r, "expected 0, got %d\n", r);
8222 test_dm_messages(0, 1, 0);
8224 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8225 ok(2 == lcount, "expected 2, got %d\n", lcount);
8227 memset(&dm_messages, 0, sizeof(dm_messages));
8228 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8229 ok(0 == r, "expected 0, got %d\n", r);
8230 test_dm_messages(0, 0, 0);
8232 memset(&dm_messages, 0, sizeof(dm_messages));
8233 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8234 ok(0 == r, "expected 0, got %d\n", r);
8235 test_dm_messages(0, 0, 1);
8237 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8238 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8239 ok(hwButton!=NULL, "CreateWindow failed with error code %ld\n", GetLastError());
8241 memset(&dm_messages, 0, sizeof(dm_messages));
8242 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8243 ok(0 == r, "expected 0, got %d\n", r);
8244 test_dm_messages(0, 1, 1);
8246 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8247 ok(2 == lcount, "expected 2, got %d\n", lcount);
8249 DestroyWindow(hwButton);
8250 DestroyWindow(hwRichEdit);
8252 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
8254 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
8256 memset(&dm_messages, 0, sizeof(dm_messages));
8257 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8258 ok(0 == r, "expected 0, got %d\n", r);
8259 test_dm_messages(0, 0, 0);
8261 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8262 ok(2 == lcount, "expected 2, got %d\n", lcount);
8264 memset(&dm_messages, 0, sizeof(dm_messages));
8265 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8266 ok(0 == r, "expected 0, got %d\n", r);
8267 test_dm_messages(0, 0, 0);
8269 memset(&dm_messages, 0, sizeof(dm_messages));
8270 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8271 ok(0 == r, "expected 0, got %d\n", r);
8272 test_dm_messages(0, 0, 0);
8274 memset(&dm_messages, 0, sizeof(dm_messages));
8275 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8276 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
8277 test_dm_messages(0, 0, 0);
8279 memset(&dm_messages, 0, sizeof(dm_messages));
8280 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8281 ok(0 == r, "expected 0, got %d\n", r);
8282 test_dm_messages(0, 0, 0);
8284 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8285 ok(3 == lcount, "expected 3, got %d\n", lcount);
8287 memset(&dm_messages, 0, sizeof(dm_messages));
8288 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8289 ok(0 == r, "expected 0, got %d\n", r);
8290 test_dm_messages(0, 0, 0);
8292 memset(&dm_messages, 0, sizeof(dm_messages));
8293 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8294 ok(0 == r, "expected 0, got %d\n", r);
8295 test_dm_messages(0, 0, 1);
8297 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8298 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8299 ok(hwButton!=NULL, "CreateWindow failed with error code %ld\n", GetLastError());
8301 memset(&dm_messages, 0, sizeof(dm_messages));
8302 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8303 ok(0 == r, "expected 0, got %d\n", r);
8304 test_dm_messages(0, 0, 0);
8306 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
8307 ok(4 == lcount, "expected 4, got %d\n", lcount);
8309 DestroyWindow(hwButton);
8310 DestroyWindow(hwRichEdit);
8312 /* Check messages from richedit(0) */
8314 hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
8316 memset(&dm_messages, 0, sizeof(dm_messages));
8317 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8318 ok(0 == r, "expected 0, got %d\n", r);
8319 test_dm_messages(0, 0, 0);
8321 memset(&dm_messages, 0, sizeof(dm_messages));
8322 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8323 ok(0 == r, "expected 0, got %d\n", r);
8324 test_dm_messages(0, 0, 0);
8326 memset(&dm_messages, 0, sizeof(dm_messages));
8327 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8328 ok(0 == r, "expected 0, got %d\n", r);
8329 test_dm_messages(0, 0, 0);
8331 memset(&dm_messages, 0, sizeof(dm_messages));
8332 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8333 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
8334 test_dm_messages(0, 0, 0);
8336 memset(&dm_messages, 0, sizeof(dm_messages));
8337 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8338 ok(0 == r, "expected 0, got %d\n", r);
8339 test_dm_messages(0, 1, 0);
8341 memset(&dm_messages, 0, sizeof(dm_messages));
8342 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
8343 ok(0 == r, "expected 0, got %d\n", r);
8344 test_dm_messages(0, 0, 0);
8346 memset(&dm_messages, 0, sizeof(dm_messages));
8347 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
8348 ok(0 == r, "expected 0, got %d\n", r);
8349 test_dm_messages(0, 0, 1);
8351 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8352 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8353 ok(hwButton!=NULL, "CreateWindow failed with error code %ld\n", GetLastError());
8355 memset(&dm_messages, 0, sizeof(dm_messages));
8356 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8357 ok(0 == r, "expected 0, got %d\n", r);
8358 test_dm_messages(0, 1, 1);
8360 DestroyWindow(hwRichEdit);
8362 /* Check messages from richedit(ES_WANTRETURN) */
8364 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
8366 memset(&dm_messages, 0, sizeof(dm_messages));
8367 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8368 ok(0 == r, "expected 0, got %d\n", r);
8369 test_dm_messages(0, 0, 0);
8371 memset(&dm_messages, 0, sizeof(dm_messages));
8372 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
8373 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
8374 test_dm_messages(0, 0, 0);
8376 memset(&dm_messages, 0, sizeof(dm_messages));
8377 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8378 ok(0 == r, "expected 0, got %d\n", r);
8379 test_dm_messages(0, 0, 0);
8381 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
8382 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
8383 ok(hwButton!=NULL, "CreateWindow failed with error code %ld\n", GetLastError());
8385 memset(&dm_messages, 0, sizeof(dm_messages));
8386 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
8387 ok(0 == r, "expected 0, got %d\n", r);
8388 test_dm_messages(0, 0, 0);
8390 DestroyWindow(hwRichEdit);
8391 DestroyWindow(hwParent);
8394 static void test_EM_FINDWORDBREAK_W(void)
8396 static const struct {
8397 WCHAR c;
8398 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
8399 } delimiter_tests[] = {
8400 {0x0a, FALSE}, /* newline */
8401 {0x0b, FALSE}, /* vertical tab */
8402 {0x0c, FALSE}, /* form feed */
8403 {0x0d, FALSE}, /* carriage return */
8404 {0x20, TRUE}, /* space */
8405 {0x61, FALSE}, /* capital letter a */
8406 {0xa0, FALSE}, /* no-break space */
8407 {0x2000, FALSE}, /* en quad */
8408 {0x3000, FALSE}, /* Ideographic space */
8409 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
8410 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
8411 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
8412 {0xac00, FALSE}, /* Hangul character GA*/
8413 {0xd7af, FALSE}, /* End of Hangul character chart */
8414 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
8415 {0xff20, FALSE}, /* fullwidth commercial @ */
8416 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
8418 int i;
8419 HWND hwndRichEdit = new_richeditW(NULL);
8420 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
8421 for (i = 0; i < ARRAY_SIZE(delimiter_tests); i++)
8423 WCHAR wbuf[2];
8424 int result;
8426 wbuf[0] = delimiter_tests[i].c;
8427 wbuf[1] = 0;
8428 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
8429 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
8430 todo_wine_if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
8431 ok(result == delimiter_tests[i].isdelimiter,
8432 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
8433 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8435 DestroyWindow(hwndRichEdit);
8438 static void test_EM_FINDWORDBREAK_A(void)
8440 static const struct {
8441 WCHAR c;
8442 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
8443 } delimiter_tests[] = {
8444 {0x0a, FALSE}, /* newline */
8445 {0x0b, FALSE}, /* vertical tab */
8446 {0x0c, FALSE}, /* form feed */
8447 {0x0d, FALSE}, /* carriage return */
8448 {0x20, TRUE}, /* space */
8449 {0x61, FALSE}, /* capital letter a */
8451 int i;
8452 HWND hwndRichEdit = new_richedit(NULL);
8454 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
8455 for (i = 0; i < ARRAY_SIZE(delimiter_tests); i++)
8457 int result;
8458 char buf[2];
8459 buf[0] = delimiter_tests[i].c;
8460 buf[1] = 0;
8461 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
8462 result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
8463 todo_wine_if (buf[0] == 0x20)
8464 ok(result == delimiter_tests[i].isdelimiter,
8465 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
8466 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
8468 DestroyWindow(hwndRichEdit);
8471 static void format_test_result(char *target, const char *src)
8473 int i;
8474 for (i = 0; i < strlen(src); i++)
8475 sprintf(target + 2*i, "%02x", src[i] & 0xFF);
8476 target[2*i] = 0;
8480 * This test attempts to show the effect of enter on a richedit
8481 * control v1.0 inserts CRLF whereas for higher versions it only
8482 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
8483 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
8484 * does for higher. The same test is cloned in riched32 and riched20.
8485 * Also shows the difference between WM_CHAR/WM_KEYDOWN in v1.0 and higher versions
8487 static void test_enter(void)
8489 static const struct {
8490 const char *initialtext;
8491 const int cursor;
8492 const char *expectedwmtext;
8493 const char *expectedemtext;
8494 const char *expectedemtextcrlf;
8495 } testenteritems[] = {
8496 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
8497 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
8498 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
8499 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
8500 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
8503 char expectedbuf[1024];
8504 char resultbuf[1024];
8505 HWND hwndRichEdit = new_richedit(NULL);
8506 UINT i;
8507 char buf[1024] = {0};
8508 GETTEXTEX getText = {sizeof(buf)};
8509 LRESULT result;
8510 const char *expected;
8512 for (i = 0; i < ARRAY_SIZE(testenteritems); i++)
8514 /* Set the text to the initial text */
8515 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
8516 ok (result == 1, "[%d] WM_SETTEXT returned %Id instead of 1\n", i, result);
8518 /* Send Enter */
8519 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
8520 simulate_typing_characters(hwndRichEdit, "\r");
8522 /* 1. Retrieve with WM_GETTEXT */
8523 buf[0] = 0x00;
8524 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
8525 expected = testenteritems[i].expectedwmtext;
8527 format_test_result(resultbuf, buf);
8528 format_test_result(expectedbuf, expected);
8530 result = strcmp(expected, buf);
8531 ok (result == 0,
8532 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
8533 i, resultbuf, expectedbuf);
8535 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
8536 getText.flags = GT_DEFAULT;
8537 getText.codepage = CP_ACP;
8538 buf[0] = 0x00;
8539 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8540 expected = testenteritems[i].expectedemtext;
8542 format_test_result(resultbuf, buf);
8543 format_test_result(expectedbuf, expected);
8545 result = strcmp(expected, buf);
8546 ok (result == 0,
8547 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
8548 i, resultbuf, expectedbuf);
8550 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
8551 getText.flags = GT_USECRLF;
8552 getText.codepage = CP_ACP;
8553 buf[0] = 0x00;
8554 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8555 expected = testenteritems[i].expectedemtextcrlf;
8557 format_test_result(resultbuf, buf);
8558 format_test_result(expectedbuf, expected);
8560 result = strcmp(expected, buf);
8561 ok (result == 0,
8562 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
8563 i, resultbuf, expectedbuf);
8566 /* Show that WM_CHAR is handled differently from WM_KEYDOWN */
8567 getText.flags = GT_DEFAULT;
8568 getText.codepage = CP_ACP;
8570 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
8571 ok (result == 1, "[%d] WM_SETTEXT returned %Id instead of 1\n", i, result);
8572 SendMessageW(hwndRichEdit, WM_CHAR, 'T', 0);
8573 SendMessageW(hwndRichEdit, WM_KEYDOWN, VK_RETURN, 0);
8575 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8576 ok(result == 2, "Got %d\n", (int)result);
8577 format_test_result(resultbuf, buf);
8578 format_test_result(expectedbuf, "T\r");
8579 result = strcmp(resultbuf, expectedbuf);
8580 ok (result == 0, "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n", i, resultbuf, expectedbuf);
8582 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
8583 ok (result == 1, "[%d] WM_SETTEXT returned %Id instead of 1\n", i, result);
8584 SendMessageW(hwndRichEdit, WM_CHAR, 'T', 0);
8585 SendMessageW(hwndRichEdit, WM_CHAR, '\r', 0);
8587 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8588 ok(result == 1, "Got %d\n", (int)result);
8589 format_test_result(resultbuf, buf);
8590 format_test_result(expectedbuf, "T");
8591 result = strcmp(resultbuf, expectedbuf);
8592 ok (result == 0, "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n", i, resultbuf, expectedbuf);
8594 DestroyWindow(hwndRichEdit);
8597 static void test_WM_CREATE(void)
8599 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
8600 static const char title[] = "line1\nline2";
8602 HWND rich_edit;
8603 LRESULT res;
8604 char buf[64];
8605 int len;
8607 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
8608 0, 0, 200, 80, NULL, NULL, NULL, NULL);
8609 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
8611 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8612 ok(len == 5, "GetWindowText returned %d\n", len);
8613 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
8615 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8616 ok(res == 0, "SendMessage(EM_GETSEL) returned %Ix\n", res);
8618 DestroyWindow(rich_edit);
8620 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
8621 0, 0, 200, 80, NULL, NULL, NULL, NULL);
8622 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
8624 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8625 ok(len == 12, "GetWindowText returned %d\n", len);
8626 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
8628 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8629 ok(res == 0, "SendMessage(EM_GETSEL) returned %Ix\n", res);
8631 DestroyWindow(rich_edit);
8634 /*******************************************************************
8635 * Test that after deleting all of the text, the first paragraph
8636 * format reverts to the default.
8638 static void test_reset_default_para_fmt( void )
8640 HWND richedit = new_richeditW( NULL );
8641 PARAFORMAT2 fmt;
8642 WORD def_align, new_align;
8644 memset( &fmt, 0, sizeof(fmt) );
8645 fmt.cbSize = sizeof(PARAFORMAT2);
8646 fmt.dwMask = -1;
8647 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8648 def_align = fmt.wAlignment;
8649 new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8651 simulate_typing_characters( richedit, "123" );
8653 SendMessageA( richedit, EM_SETSEL, 0, -1 );
8654 fmt.dwMask = PFM_ALIGNMENT;
8655 fmt.wAlignment = new_align;
8656 SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
8658 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8659 ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
8661 SendMessageA( richedit, EM_SETSEL, 0, -1 );
8662 SendMessageA( richedit, WM_CUT, 0, 0 );
8664 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8665 ok( fmt.wAlignment == def_align, "got %d expect %d\n", fmt.wAlignment, def_align );
8667 DestroyWindow( richedit );
8670 static void test_EM_SETREADONLY(void)
8672 HWND richedit = new_richeditW(NULL);
8673 DWORD dwStyle;
8674 LRESULT res;
8676 res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
8677 ok(res == 1, "EM_SETREADONLY\n");
8678 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8679 ok(dwStyle & ES_READONLY, "got wrong value: 0x%lx\n", dwStyle);
8681 res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
8682 ok(res == 1, "EM_SETREADONLY\n");
8683 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8684 ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%lx\n", dwStyle);
8686 DestroyWindow(richedit);
8689 static inline LONG twips2points(LONG value)
8691 return value / 20;
8694 #define TEST_EM_SETFONTSIZE(hwnd,size,expected_size,expected_res,expected_undo) \
8695 _test_font_size(__LINE__,hwnd,size,expected_size,expected_res,expected_undo)
8696 static void _test_font_size(unsigned line, HWND hwnd, LONG size, LONG expected_size,
8697 LRESULT expected_res, BOOL expected_undo)
8699 CHARFORMAT2A cf;
8700 LRESULT res;
8701 BOOL isundo;
8703 cf.cbSize = sizeof(cf);
8704 cf.dwMask = CFM_SIZE;
8706 res = SendMessageA(hwnd, EM_SETFONTSIZE, size, 0);
8707 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
8708 isundo = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
8709 ok_(__FILE__,line)(res == expected_res, "EM_SETFONTSIZE unexpected return value: %Ix.\n", res);
8710 ok_(__FILE__,line)(twips2points(cf.yHeight) == expected_size, "got wrong font size: %ld, expected: %ld\n",
8711 twips2points(cf.yHeight), expected_size);
8712 ok_(__FILE__,line)(isundo == expected_undo, "get wrong undo mark: %d, expected: %d.\n",
8713 isundo, expected_undo);
8716 static void test_EM_SETFONTSIZE(void)
8718 HWND richedit = new_richedit(NULL);
8719 CHAR text[] = "wine";
8720 CHARFORMAT2A tmp_cf;
8721 LONG default_size;
8723 tmp_cf.cbSize = sizeof(tmp_cf);
8724 tmp_cf.dwMask = CFM_SIZE;
8725 tmp_cf.yHeight = 9 * 20.0;
8726 SendMessageA(richedit, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8728 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)text);
8730 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8731 /* without selection */
8732 TEST_EM_SETFONTSIZE(richedit, 1, 10, TRUE, FALSE); /* 9 + 1 -> 10 */
8733 SendMessageA(richedit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8734 default_size = twips2points(tmp_cf.yHeight);
8735 ok(default_size == 9, "Default font size should not be changed.\n");
8736 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8738 SendMessageA(richedit, EM_SETSEL, 0, 2);
8740 TEST_EM_SETFONTSIZE(richedit, 0, 9, TRUE, TRUE); /* 9 + 0 -> 9 */
8742 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8743 TEST_EM_SETFONTSIZE(richedit, 3, 12, TRUE, TRUE); /* 9 + 3 -> 12 */
8744 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8746 TEST_EM_SETFONTSIZE(richedit, 1, 14, TRUE, TRUE); /* 12 + 1 + 1 -> 14 */
8747 TEST_EM_SETFONTSIZE(richedit, -1, 12, TRUE, TRUE); /* 14 - 1 - 1 -> 12 */
8748 TEST_EM_SETFONTSIZE(richedit, 4, 16, TRUE, TRUE); /* 12 + 4 -> 16 */
8749 TEST_EM_SETFONTSIZE(richedit, 3, 20, TRUE, TRUE); /* 16 + 3 + 1 -> 20 */
8750 TEST_EM_SETFONTSIZE(richedit, 0, 20, TRUE, TRUE); /* 20 + 0 -> 20 */
8751 TEST_EM_SETFONTSIZE(richedit, 8, 28, TRUE, TRUE); /* 20 + 8 -> 28 */
8752 TEST_EM_SETFONTSIZE(richedit, 0, 28, TRUE, TRUE); /* 28 + 0 -> 28 */
8753 TEST_EM_SETFONTSIZE(richedit, 1, 36, TRUE, TRUE); /* 28 + 1 -> 36 */
8754 TEST_EM_SETFONTSIZE(richedit, 0, 36, TRUE, TRUE); /* 36 + 0 -> 36 */
8755 TEST_EM_SETFONTSIZE(richedit, 1, 48, TRUE, TRUE); /* 36 + 1 -> 48 */
8756 TEST_EM_SETFONTSIZE(richedit, 0, 48, TRUE, TRUE); /* 48 + 0 -> 48 */
8757 TEST_EM_SETFONTSIZE(richedit, 1, 72, TRUE, TRUE); /* 48 + 1 -> 72 */
8758 TEST_EM_SETFONTSIZE(richedit, 0, 72, TRUE, TRUE); /* 72 + 0 -> 72 */
8759 TEST_EM_SETFONTSIZE(richedit, 1, 80, TRUE, TRUE); /* 72 + 1 -> 80 */
8760 TEST_EM_SETFONTSIZE(richedit, 0, 80, TRUE, TRUE); /* 80 + 0 -> 80 */
8761 TEST_EM_SETFONTSIZE(richedit, 1, 90, TRUE, TRUE); /* 80 + 1 -> 90 */
8762 TEST_EM_SETFONTSIZE(richedit, 0, 90, TRUE, TRUE); /* 90 + 0 -> 90 */
8763 TEST_EM_SETFONTSIZE(richedit, 1, 100, TRUE, TRUE); /* 90 + 1 -> 100 */
8764 TEST_EM_SETFONTSIZE(richedit, 25, 130, TRUE, TRUE); /* 100 + 25 -> 130 */
8765 TEST_EM_SETFONTSIZE(richedit, -1, 120, TRUE, TRUE); /* 130 - 1 -> 120 */
8766 TEST_EM_SETFONTSIZE(richedit, -35, 80, TRUE, TRUE); /* 120 - 35 -> 80 */
8767 TEST_EM_SETFONTSIZE(richedit, -7, 72, TRUE, TRUE); /* 80 - 7 -> 72 */
8768 TEST_EM_SETFONTSIZE(richedit, -42, 28, TRUE, TRUE); /* 72 - 42 -> 28 */
8769 TEST_EM_SETFONTSIZE(richedit, -16, 12, TRUE, TRUE); /* 28 - 16 -> 12 */
8770 TEST_EM_SETFONTSIZE(richedit, -3, 9, TRUE, TRUE); /* 12 - 3 -> 9 */
8771 TEST_EM_SETFONTSIZE(richedit, -8, 1, TRUE, TRUE); /* 9 - 8 -> 1 */
8772 TEST_EM_SETFONTSIZE(richedit, -111, 1, TRUE, TRUE); /* 1 - 111 -> 1 */
8773 TEST_EM_SETFONTSIZE(richedit, 10086, 1638, TRUE, TRUE); /* 1 + 10086 -> 1638 */
8775 /* return FALSE when richedit is TM_PLAINTEXT mode */
8776 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)"");
8777 SendMessageA(richedit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
8778 TEST_EM_SETFONTSIZE(richedit, 0, 9, FALSE, FALSE);
8780 DestroyWindow(richedit);
8783 static void test_alignment_style(void)
8785 HWND richedit = NULL;
8786 PARAFORMAT2 pf;
8787 DWORD align_style[] = {ES_LEFT, ES_CENTER, ES_RIGHT, ES_RIGHT | ES_CENTER,
8788 ES_LEFT | ES_CENTER, ES_LEFT | ES_RIGHT,
8789 ES_LEFT | ES_RIGHT | ES_CENTER};
8790 DWORD align_mask[] = {PFA_LEFT, PFA_CENTER, PFA_RIGHT, PFA_CENTER, PFA_CENTER,
8791 PFA_RIGHT, PFA_CENTER};
8792 const char * streamtext =
8793 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
8794 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
8795 "}\r\n";
8796 EDITSTREAM es;
8797 int i;
8799 for (i = 0; i < ARRAY_SIZE(align_style); i++)
8801 DWORD dwStyle, new_align;
8803 richedit = new_windowW(RICHEDIT_CLASS20W, align_style[i], NULL);
8804 memset(&pf, 0, sizeof(pf));
8805 pf.cbSize = sizeof(PARAFORMAT2);
8806 pf.dwMask = -1;
8808 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8809 ok(pf.wAlignment == align_mask[i], "(i = %d) got %d expected %ld\n",
8810 i, pf.wAlignment, align_mask[i]);
8811 dwStyle = GetWindowLongW(richedit, GWL_STYLE);
8812 ok((i ? (dwStyle & align_style[i]) : (!(dwStyle & 0x0000000f))) ,
8813 "(i = %d) didn't set right align style: 0x%lx\n", i, dwStyle);
8816 /* Based on test_reset_default_para_fmt() */
8817 new_align = (align_mask[i] == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8818 simulate_typing_characters(richedit, "123");
8820 SendMessageW(richedit, EM_SETSEL, 0, -1);
8821 pf.dwMask = PFM_ALIGNMENT;
8822 pf.wAlignment = new_align;
8823 SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8825 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8826 ok(pf.wAlignment == new_align, "got %d expect %ld\n", pf.wAlignment, new_align);
8828 SendMessageW(richedit, EM_SETSEL, 0, -1);
8829 SendMessageW(richedit, WM_CUT, 0, 0);
8831 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8832 ok(pf.wAlignment == align_mask[i], "got %d expect %ld\n", pf.wAlignment, align_mask[i]);
8834 DestroyWindow(richedit);
8837 /* test with EM_STREAMIN */
8838 richedit = new_windowW(RICHEDIT_CLASS20W, ES_CENTER, NULL);
8839 simulate_typing_characters(richedit, "abc");
8840 es.dwCookie = (DWORD_PTR)&streamtext;
8841 es.dwError = 0;
8842 es.pfnCallback = test_EM_STREAMIN_esCallback;
8843 SendMessageW(richedit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
8844 SendMessageW(richedit, EM_SETSEL, 0, -1);
8845 memset(&pf, 0, sizeof(pf));
8846 pf.cbSize = sizeof(PARAFORMAT2);
8847 pf.dwMask = -1;
8848 SendMessageW(richedit, EM_GETPARAFORMAT, SCF_SELECTION, (LPARAM)&pf);
8849 ok(pf.wAlignment == PFA_LEFT, "got %d expected PFA_LEFT\n", pf.wAlignment);
8850 DestroyWindow(richedit);
8853 static void test_WM_GETTEXTLENGTH(void)
8855 HWND hwndRichEdit = new_richedit(NULL);
8856 static const char text1[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
8857 static const char text2[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
8858 static const char text3[] = "abcdef\x8e\xf0";
8859 int result;
8861 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
8862 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8863 ok(result == lstrlenA(text1), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8864 result, lstrlenA(text1));
8866 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
8867 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8868 ok(result == lstrlenA(text2), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8869 result, lstrlenA(text2));
8871 /* Test with multibyte character */
8872 if (!is_lang_japanese)
8873 skip("Skip multibyte character tests on non-Japanese platform\n");
8874 else
8876 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
8877 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8878 todo_wine ok(result == 8, "WM_GETTEXTLENGTH returned %d, expected 8\n", result);
8881 DestroyWindow(hwndRichEdit);
8884 static void test_rtf(void)
8886 const char *specials = "{\\rtf1\\emspace\\enspace\\bullet\\lquote"
8887 "\\rquote\\ldblquote\\rdblquote\\ltrmark\\rtlmark\\zwj\\zwnj}";
8888 const WCHAR expect_specials[] = {' ',' ',0x2022,0x2018,0x2019,0x201c,
8889 0x201d,0x200e,0x200f,0x200d,0x200c};
8890 const char *pard = "{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}";
8891 const char *highlight = "{\\rtf1{\\colortbl;\\red0\\green0\\blue0;\\red128\\green128\\blue128;\\red192\\green192\\blue192;}\\cf2\\highlight3 foo\\par}";
8893 HWND edit = new_richeditW( NULL );
8894 EDITSTREAM es;
8895 WCHAR buf[80];
8896 LRESULT result;
8897 PARAFORMAT2 fmt;
8898 CHARFORMAT2W cf;
8900 /* Test rtf specials */
8901 es.dwCookie = (DWORD_PTR)&specials;
8902 es.dwError = 0;
8903 es.pfnCallback = test_EM_STREAMIN_esCallback;
8904 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8905 ok( result == 11, "got %Id\n", result );
8907 result = SendMessageW( edit, WM_GETTEXT, ARRAY_SIZE(buf), (LPARAM)buf );
8908 ok( result == ARRAY_SIZE(expect_specials), "got %Id\n", result );
8909 ok( !memcmp( buf, expect_specials, sizeof(expect_specials) ), "got %s\n", wine_dbgstr_w(buf) );
8911 /* Show that \rtlpar propagates to the second paragraph and is
8912 reset by \pard in the third. */
8913 es.dwCookie = (DWORD_PTR)&pard;
8914 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8915 ok( result == 11, "got %Id\n", result );
8917 fmt.cbSize = sizeof(fmt);
8918 SendMessageW( edit, EM_SETSEL, 1, 1 );
8919 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8920 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8921 ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8922 SendMessageW( edit, EM_SETSEL, 5, 5 );
8923 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8924 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8925 ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8926 SendMessageW( edit, EM_SETSEL, 9, 9 );
8927 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8928 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8929 ok( !(fmt.wEffects & PFE_RTLPARA), "rtl para set\n" );
8931 /* Test \highlight */
8932 es.dwCookie = (DWORD_PTR)&highlight;
8933 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8934 ok( result == 3, "got %Id\n", result );
8935 SendMessageW( edit, EM_SETSEL, 1, 1 );
8936 memset( &cf, 0, sizeof(cf) );
8937 cf.cbSize = sizeof(cf);
8938 SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8939 ok( (cf.dwEffects & (CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR)) == 0, "got %08lx\n", cf.dwEffects );
8940 ok( cf.crTextColor == RGB(128,128,128), "got %08lx\n", cf.crTextColor );
8941 ok( cf.crBackColor == RGB(192,192,192), "got %08lx\n", cf.crBackColor );
8943 DestroyWindow( edit );
8946 static void test_background(void)
8948 HWND hwndRichEdit = new_richedit(NULL);
8950 /* set the background color to black */
8951 ValidateRect(hwndRichEdit, NULL);
8952 SendMessageA(hwndRichEdit, EM_SETBKGNDCOLOR, FALSE, RGB(0, 0, 0));
8953 ok(GetUpdateRect(hwndRichEdit, NULL, FALSE), "Update rectangle is empty!\n");
8955 DestroyWindow(hwndRichEdit);
8958 static void test_eop_char_fmt(void)
8960 HWND edit = new_richedit( NULL );
8961 const char *rtf = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8962 "{\\fs10{\\pard\\fs16\\fi200\\li360\\f0 First\\par"
8963 "\\f0\\fs25 Second\\par"
8964 "{\\f0\\fs26 Third}\\par"
8965 "{\\f0\\fs22 Fourth}\\par}}}";
8966 EDITSTREAM es;
8967 CHARFORMAT2W cf;
8968 int i, num, expect_height;
8970 es.dwCookie = (DWORD_PTR)&rtf;
8971 es.dwError = 0;
8972 es.pfnCallback = test_EM_STREAMIN_esCallback;
8973 num = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8974 ok( num == 25, "got %d\n", num );
8976 for (i = 0; i <= num; i++)
8978 SendMessageW( edit, EM_SETSEL, i, i + 1 );
8979 cf.cbSize = sizeof(cf);
8980 cf.dwMask = CFM_SIZE;
8981 SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
8982 ok( cf.dwMask & CFM_SIZE, "%d: got %08lx\n", i, cf.dwMask );
8983 if (i < 6) expect_height = 160;
8984 else if (i < 13) expect_height = 250;
8985 else if (i < 18) expect_height = 260;
8986 else if (i == 18 || i == 25) expect_height = 250;
8987 else expect_height = 220;
8988 ok( cf.yHeight == expect_height, "%d: got %ld\n", i, cf.yHeight );
8991 DestroyWindow( edit );
8994 static void test_para_numbering(void)
8996 HWND edit = new_richeditW( NULL );
8997 const char *numbers = "{\\rtf1{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Arial;}{\\f1\\fnil\\fcharset2 Symbol;}}"
8998 "\\pard{\\pntext\\f0 3.\\tab}{\\*\\pn\\pnlvlbody\\pnfs32\\pnf0\\pnindent1000\\pnstart2\\pndec{\\pntxta.}}"
8999 "\\fs20\\fi200\\li360\\f0 First\\par"
9000 "{\\pntext\\f0 4.\\tab}\\f0 Second\\par"
9001 "{\\pntext\\f0 6.\\tab}\\f0 Third\\par}";
9002 const WCHAR expect_numbers_txt[] = {'F','i','r','s','t','\r','S','e','c','o','n','d','\r','T','h','i','r','d',0};
9003 EDITSTREAM es;
9004 WCHAR buf[80];
9005 LRESULT result;
9006 PARAFORMAT2 fmt, fmt2;
9007 GETTEXTEX get_text;
9008 CHARFORMAT2W cf;
9010 get_text.cb = sizeof(buf);
9011 get_text.flags = GT_RAWTEXT;
9012 get_text.codepage = 1200;
9013 get_text.lpDefaultChar = NULL;
9014 get_text.lpUsedDefChar = NULL;
9016 es.dwCookie = (DWORD_PTR)&numbers;
9017 es.dwError = 0;
9018 es.pfnCallback = test_EM_STREAMIN_esCallback;
9019 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
9020 ok( result == lstrlenW( expect_numbers_txt ), "got %Id\n", result );
9022 result = SendMessageW( edit, EM_GETTEXTEX, (WPARAM)&get_text, (LPARAM)buf );
9023 ok( result == lstrlenW( expect_numbers_txt ), "got %Id\n", result );
9024 ok( !lstrcmpW( buf, expect_numbers_txt ), "got %s\n", wine_dbgstr_w(buf) );
9026 SendMessageW( edit, EM_SETSEL, 1, 1 );
9027 memset( &fmt, 0, sizeof(fmt) );
9028 fmt.cbSize = sizeof(fmt);
9029 fmt.dwMask = PFM_ALL2;
9030 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
9031 ok( fmt.wNumbering == PFN_ARABIC, "got %d\n", fmt.wNumbering );
9032 ok( fmt.wNumberingStart == 2, "got %d\n", fmt.wNumberingStart );
9033 ok( fmt.wNumberingStyle == PFNS_PERIOD, "got %04x\n", fmt.wNumberingStyle );
9034 ok( fmt.wNumberingTab == 1000, "got %d\n", fmt.wNumberingTab );
9035 ok( fmt.dxStartIndent == 560, "got %ld\n", fmt.dxStartIndent );
9036 ok( fmt.dxOffset == -200, "got %ld\n", fmt.dxOffset );
9038 /* Second para should have identical fmt */
9039 SendMessageW( edit, EM_SETSEL, 10, 10 );
9040 memset( &fmt2, 0, sizeof(fmt2) );
9041 fmt2.cbSize = sizeof(fmt2);
9042 fmt2.dwMask = PFM_ALL2;
9043 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt2 );
9044 ok( !memcmp( &fmt, &fmt2, sizeof(fmt) ), "format mismatch\n" );
9046 /* Check the eop heights - this determines the label height */
9047 SendMessageW( edit, EM_SETSEL, 12, 13 );
9048 cf.cbSize = sizeof(cf);
9049 cf.dwMask = CFM_SIZE;
9050 SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
9051 ok( cf.yHeight == 200, "got %ld\n", cf.yHeight );
9053 SendMessageW( edit, EM_SETSEL, 18, 19 );
9054 cf.cbSize = sizeof(cf);
9055 cf.dwMask = CFM_SIZE;
9056 SendMessageW( edit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf );
9057 ok( cf.yHeight == 200, "got %ld\n", cf.yHeight );
9059 DestroyWindow( edit );
9062 static void fill_reobject_struct(REOBJECT *reobj, LONG cp, LPOLEOBJECT poleobj,
9063 LPSTORAGE pstg, LPOLECLIENTSITE polesite, LONG sizel_cx,
9064 LONG sizel_cy, DWORD aspect, DWORD flags, DWORD user)
9066 reobj->cbStruct = sizeof(*reobj);
9067 reobj->clsid = CLSID_NULL;
9068 reobj->cp = cp;
9069 reobj->poleobj = poleobj;
9070 reobj->pstg = pstg;
9071 reobj->polesite = polesite;
9072 reobj->sizel.cx = sizel_cx;
9073 reobj->sizel.cy = sizel_cy;
9074 reobj->dvaspect = aspect;
9075 reobj->dwFlags = flags;
9076 reobj->dwUser = user;
9079 static BOOL change_received = FALSE;
9081 static LRESULT WINAPI ChangeWatcherWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
9083 if (message == WM_COMMAND && (wParam >> 16) == EN_CHANGE) change_received = TRUE;
9084 return DefWindowProcA(hwnd, message, wParam, lParam);
9087 static LRESULT WINAPI RichEditWithEventsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
9089 if (message == WM_CREATE)
9090 SendMessageA(hwnd, EM_SETEVENTMASK, 0, ENM_CHANGE);
9091 return CallWindowProcA(richeditProc, hwnd, message, wParam, lParam);
9094 static void test_init_messages(void)
9096 WNDCLASSA cls;
9097 HWND parent, edit;
9099 /* register class to capture EN_CHANGE */
9100 cls = make_simple_class(ChangeWatcherWndProc, "ChangeWatcherClass");
9101 if (!RegisterClassA(&cls)) assert(0);
9103 /* and a class that sets ENM_CHANGE during WM_CREATE */
9104 if (!GetClassInfoA(hmoduleRichEdit, RICHEDIT_CLASS20A, &cls)) return;
9105 richeditProc = cls.lpfnWndProc;
9106 cls.lpfnWndProc = RichEditWithEventsWndProc;
9107 cls.lpszClassName = "RichEditWithEvents";
9108 if (!RegisterClassA(&cls)) assert(0);
9110 parent = CreateWindowA("ChangeWatcherClass", NULL, WS_POPUP|WS_VISIBLE,
9111 0, 0, 200, 60, NULL, NULL, NULL, NULL);
9112 ok(parent != 0, "Failed to create parent window\n");
9113 change_received = FALSE;
9114 edit = new_window("RichEditWithEvents", 0, parent);
9115 ok(change_received == FALSE, "Creating a RichEdit should not make any EN_CHANGE events\n");
9116 DestroyWindow(edit);
9117 DestroyWindow(parent);
9120 static void test_EM_SELECTIONTYPE(void)
9122 HWND hwnd = new_richedit(NULL);
9123 IRichEditOle *reole = NULL;
9124 static const char text1[] = "abcdefg\n";
9125 int result;
9126 REOBJECT reo1, reo2;
9127 IOleClientSite *clientsite;
9128 HRESULT hr;
9130 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text1);
9131 SendMessageA(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&reole);
9133 SendMessageA(hwnd, EM_SETSEL, 1, 1);
9134 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
9135 ok(result == SEL_EMPTY, "got wrong selection type: %x.\n", result);
9137 SendMessageA(hwnd, EM_SETSEL, 1, 2);
9138 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
9139 ok(result == SEL_TEXT, "got wrong selection type: %x.\n", result);
9141 SendMessageA(hwnd, EM_SETSEL, 2, 5);
9142 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
9143 ok(result == (SEL_TEXT | SEL_MULTICHAR), "got wrong selection type: %x.\n", result);
9145 SendMessageA(hwnd, EM_SETSEL, 0, 1);
9146 hr = IRichEditOle_GetClientSite(reole, &clientsite);
9147 ok(hr == S_OK, "IRichEditOle_GetClientSite failed: 0x%08lx\n", hr);
9148 fill_reobject_struct(&reo1, REO_CP_SELECTION, NULL, NULL, clientsite, 10, 10,
9149 DVASPECT_CONTENT, 0, 1);
9150 hr = IRichEditOle_InsertObject(reole, &reo1);
9151 ok(hr == S_OK, "IRichEditOle_InsertObject failed: 0x%08lx\n", hr);
9152 IOleClientSite_Release(clientsite);
9154 SendMessageA(hwnd, EM_SETSEL, 0, 1);
9155 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
9156 ok(result == SEL_OBJECT, "got wrong selection type: %x.\n", result);
9158 SendMessageA(hwnd, EM_SETSEL, 0, 2);
9159 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
9160 ok(result == (SEL_TEXT | SEL_OBJECT), "got wrong selection type: %x.\n", result);
9162 SendMessageA(hwnd, EM_SETSEL, 0, 3);
9163 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
9164 ok(result == (SEL_TEXT | SEL_MULTICHAR | SEL_OBJECT), "got wrong selection type: %x.\n", result);
9166 SendMessageA(hwnd, EM_SETSEL, 2, 3);
9167 hr = IRichEditOle_GetClientSite(reole, &clientsite);
9168 ok(hr == S_OK, "IRichEditOle_GetClientSite failed: 0x%08lx\n", hr);
9169 fill_reobject_struct(&reo2, REO_CP_SELECTION, NULL, NULL, clientsite, 10, 10,
9170 DVASPECT_CONTENT, 0, 2);
9171 hr = IRichEditOle_InsertObject(reole, &reo2);
9172 ok(hr == S_OK, "IRichEditOle_InsertObject failed: 0x%08lx\n", hr);
9173 IOleClientSite_Release(clientsite);
9175 SendMessageA(hwnd, EM_SETSEL, 0, 2);
9176 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
9177 ok(result == (SEL_OBJECT | SEL_TEXT), "got wrong selection type: %x.\n", result);
9179 SendMessageA(hwnd, EM_SETSEL, 0, 3);
9180 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
9181 ok(result == (SEL_OBJECT | SEL_MULTIOBJECT | SEL_TEXT), "got wrong selection type: %x.\n", result);
9183 SendMessageA(hwnd, EM_SETSEL, 0, 4);
9184 result = SendMessageA(hwnd, EM_SELECTIONTYPE, 0, 0);
9185 ok(result == (SEL_TEXT| SEL_MULTICHAR | SEL_OBJECT | SEL_MULTIOBJECT), "got wrong selection type: %x.\n", result);
9187 IRichEditOle_Release(reole);
9188 DestroyWindow(hwnd);
9191 static void test_window_classes(void)
9193 static const struct
9195 const char *class;
9196 BOOL success;
9197 } test[] =
9199 { "RichEdit", FALSE },
9200 { "RichEdit20A", TRUE },
9201 { "RichEdit20W", TRUE },
9202 { "RichEdit50A", FALSE },
9203 { "RichEdit50W", FALSE }
9205 int i;
9206 HWND hwnd;
9208 for (i = 0; i < ARRAY_SIZE(test); i++)
9210 SetLastError(0xdeadbeef);
9211 hwnd = CreateWindowExA(0, test[i].class, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, NULL);
9212 todo_wine_if(!strcmp(test[i].class, "RichEdit50A") || !strcmp(test[i].class, "RichEdit50W"))
9213 ok(!hwnd == !test[i].success, "CreateWindow(%s) should %s\n",
9214 test[i].class, test[i].success ? "succeed" : "fail");
9215 if (!hwnd)
9216 todo_wine
9217 ok(GetLastError() == ERROR_CANNOT_FIND_WND_CLASS, "got %lu\n", GetLastError());
9218 else
9219 DestroyWindow(hwnd);
9223 START_TEST( editor )
9225 BOOL ret;
9226 /* Must explicitly LoadLibrary(). The test has no reference to functions in
9227 * RICHED20.DLL, so the linker does not actually link to it. */
9228 hmoduleRichEdit = LoadLibraryA("riched20.dll");
9229 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
9230 is_lang_japanese = (PRIMARYLANGID(GetSystemDefaultLangID()) == LANG_JAPANESE);
9232 test_window_classes();
9233 test_WM_CHAR();
9234 test_EM_FINDTEXT(FALSE);
9235 test_EM_FINDTEXT(TRUE);
9236 test_EM_GETLINE();
9237 test_EM_POSFROMCHAR();
9238 test_EM_SCROLLCARET();
9239 test_EM_SCROLL();
9240 test_scrollbar_visibility();
9241 test_WM_SETTEXT();
9242 test_EM_LINELENGTH();
9243 test_EM_SETCHARFORMAT();
9244 test_EM_SETTEXTMODE();
9245 test_TM_PLAINTEXT();
9246 test_EM_SETOPTIONS();
9247 test_WM_GETTEXT();
9248 test_EM_GETTEXTRANGE();
9249 test_EM_GETSELTEXT();
9250 test_EM_SETUNDOLIMIT();
9251 test_ES_PASSWORD();
9252 test_EM_SETTEXTEX();
9253 test_EM_LIMITTEXT();
9254 test_EM_EXLIMITTEXT();
9255 test_EM_GETLIMITTEXT();
9256 test_WM_SETFONT();
9257 test_EM_GETMODIFY();
9258 test_EM_SETSEL();
9259 test_EM_EXSETSEL();
9260 test_WM_PASTE();
9261 test_EM_STREAMIN();
9262 test_EM_STREAMOUT();
9263 test_EM_STREAMOUT_FONTTBL();
9264 test_EM_STREAMOUT_empty_para();
9265 test_EM_StreamIn_Undo();
9266 test_EM_FORMATRANGE();
9267 test_unicode_conversions();
9268 test_EM_GETTEXTLENGTHEX();
9269 test_WM_GETTEXTLENGTH();
9270 test_EM_REPLACESEL(1);
9271 test_EM_REPLACESEL(0);
9272 test_WM_NOTIFY();
9273 test_EN_LINK();
9274 test_EM_AUTOURLDETECT();
9275 test_eventMask();
9276 test_undo_coalescing();
9277 test_word_movement();
9278 test_word_movement_multiline();
9279 test_EM_CHARFROMPOS();
9280 test_SETPARAFORMAT();
9281 test_word_wrap();
9282 test_autoscroll();
9283 test_format_rect();
9284 test_WM_GETDLGCODE();
9285 test_zoom();
9286 test_dialogmode();
9287 test_EM_FINDWORDBREAK_W();
9288 test_EM_FINDWORDBREAK_A();
9289 test_enter();
9290 test_WM_CREATE();
9291 test_reset_default_para_fmt();
9292 test_EM_SETREADONLY();
9293 test_EM_SETFONTSIZE();
9294 test_alignment_style();
9295 test_rtf();
9296 test_background();
9297 test_eop_char_fmt();
9298 test_para_numbering();
9299 test_init_messages();
9300 test_EM_SELECTIONTYPE();
9302 /* Set the environment variable WINETEST_RICHED20 to keep windows
9303 * responsive and open for 30 seconds. This is useful for debugging.
9305 if (getenv( "WINETEST_RICHED20" )) {
9306 keep_responsive(30);
9309 OleFlushClipboard();
9310 ret = FreeLibrary(hmoduleRichEdit);
9311 ok(ret, "error: %d\n", (int) GetLastError());