richedit: Return number of bytes read for EM_STREAMIN with SF_TEXT.
[wine/multimedia.git] / dlls / riched20 / tests / editor.c
blob5058b7a8da06c697d2a8b936b88f6d0e13d12817
1 /*
2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
36 #define ID_RICHEDITTESTDBUTTON 0x123
38 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
40 #define ok_w3(format, szString1, szString2, szString3) \
41 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
42 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
43 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
44 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
45 format, string1, string2, string3);
47 static HMODULE hmoduleRichEdit;
49 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
50 HWND hwnd;
51 hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
52 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
53 hmoduleRichEdit, NULL);
54 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
55 return hwnd;
58 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
59 HWND hwnd;
60 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
61 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
62 hmoduleRichEdit, NULL);
63 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
64 return hwnd;
67 static HWND new_richedit(HWND parent) {
68 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
71 static HWND new_richeditW(HWND parent) {
72 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
75 /* Keeps the window reponsive for the deley_time in seconds.
76 * This is useful for debugging a test to see what is happening. */
77 static void keep_responsive(time_t delay_time)
79 MSG msg;
80 time_t end;
82 /* The message pump uses PeekMessage() to empty the queue and then
83 * sleeps for 50ms before retrying the queue. */
84 end = time(NULL) + delay_time;
85 while (time(NULL) < end) {
86 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
87 TranslateMessage(&msg);
88 DispatchMessage(&msg);
89 } else {
90 Sleep(50);
95 static void processPendingMessages(void)
97 MSG msg;
98 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
99 TranslateMessage(&msg);
100 DispatchMessage(&msg);
104 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
106 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
107 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
108 SetFocus(hwnd);
109 keybd_event(mod_vk, mod_scan_code, 0, 0);
110 keybd_event(vk, scan_code, 0, 0);
111 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
112 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
113 processPendingMessages();
116 static void simulate_typing_characters(HWND hwnd, const char* szChars)
118 int ret;
120 while (*szChars != '\0') {
121 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
122 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
123 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
124 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
125 szChars++;
129 static BOOL hold_key(int vk)
131 BYTE key_state[256];
132 BOOL result;
134 result = GetKeyboardState(key_state);
135 ok(result, "GetKeyboardState failed.\n");
136 if (!result) return FALSE;
137 key_state[vk] |= 0x80;
138 result = SetKeyboardState(key_state);
139 ok(result, "SetKeyboardState failed.\n");
140 return result != 0;
143 static BOOL release_key(int vk)
145 BYTE key_state[256];
146 BOOL result;
148 result = GetKeyboardState(key_state);
149 ok(result, "GetKeyboardState failed.\n");
150 if (!result) return FALSE;
151 key_state[vk] &= ~0x80;
152 result = SetKeyboardState(key_state);
153 ok(result, "SetKeyboardState failed.\n");
154 return result != 0;
157 static const char haystack[] = "WINEWine wineWine wine WineWine";
158 /* ^0 ^10 ^20 ^30 */
160 struct find_s {
161 int start;
162 int end;
163 const char *needle;
164 int flags;
165 int expected_loc;
169 static struct find_s find_tests[] = {
170 /* Find in empty text */
171 {0, -1, "foo", FR_DOWN, -1},
172 {0, -1, "foo", 0, -1},
173 {0, -1, "", FR_DOWN, -1},
174 {20, 5, "foo", FR_DOWN, -1},
175 {5, 20, "foo", FR_DOWN, -1}
178 static struct find_s find_tests2[] = {
179 /* No-result find */
180 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
181 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
183 /* Subsequent finds */
184 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
185 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
186 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
187 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
189 /* Find backwards */
190 {19, 20, "Wine", FR_MATCHCASE, 13},
191 {10, 20, "Wine", FR_MATCHCASE, 4},
192 {20, 10, "Wine", FR_MATCHCASE, 13},
194 /* Case-insensitive */
195 {1, 31, "wInE", FR_DOWN, 4},
196 {1, 31, "Wine", FR_DOWN, 4},
198 /* High-to-low ranges */
199 {20, 5, "Wine", FR_DOWN, -1},
200 {2, 1, "Wine", FR_DOWN, -1},
201 {30, 29, "Wine", FR_DOWN, -1},
202 {20, 5, "Wine", 0, 13},
204 /* Find nothing */
205 {5, 10, "", FR_DOWN, -1},
206 {10, 5, "", FR_DOWN, -1},
207 {0, -1, "", FR_DOWN, -1},
208 {10, 5, "", 0, -1},
210 /* Whole-word search */
211 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
212 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
213 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
214 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
215 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
216 {11, -1, "winewine", FR_WHOLEWORD, 0},
217 {31, -1, "winewine", FR_WHOLEWORD, 23},
219 /* Bad ranges */
220 {5, 200, "XXX", FR_DOWN, -1},
221 {-20, 20, "Wine", FR_DOWN, -1},
222 {-20, 20, "Wine", FR_DOWN, -1},
223 {-15, -20, "Wine", FR_DOWN, -1},
224 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
226 /* Check the case noted in bug 4479 where matches at end aren't recognized */
227 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
228 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
229 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
230 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
231 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
233 /* The backwards case of bug 4479; bounds look right
234 * Fails because backward find is wrong */
235 {19, 20, "WINE", FR_MATCHCASE, 0},
236 {0, 20, "WINE", FR_MATCHCASE, -1},
238 {0, -1, "wineWine wine", 0, -1},
241 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
242 int findloc;
243 FINDTEXT ft;
244 memset(&ft, 0, sizeof(ft));
245 ft.chrg.cpMin = f->start;
246 ft.chrg.cpMax = f->end;
247 ft.lpstrText = f->needle;
248 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
249 ok(findloc == f->expected_loc,
250 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
251 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
254 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
255 int id) {
256 int findloc;
257 FINDTEXTEX ft;
258 int expected_end_loc;
260 memset(&ft, 0, sizeof(ft));
261 ft.chrg.cpMin = f->start;
262 ft.chrg.cpMax = f->end;
263 ft.lpstrText = f->needle;
264 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
265 ok(findloc == f->expected_loc,
266 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
267 name, id, f->needle, f->start, f->end, f->flags, findloc);
268 ok(ft.chrgText.cpMin == f->expected_loc,
269 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
270 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
271 expected_end_loc = ((f->expected_loc == -1) ? -1
272 : f->expected_loc + strlen(f->needle));
273 ok(ft.chrgText.cpMax == expected_end_loc,
274 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
275 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
278 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
279 int num_tests)
281 int i;
283 for (i = 0; i < num_tests; i++) {
284 check_EM_FINDTEXT(hwnd, name, &find[i], i);
285 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
289 static void test_EM_FINDTEXT(void)
291 HWND hwndRichEdit = new_richedit(NULL);
292 CHARFORMAT2 cf2;
294 /* Empty rich edit control */
295 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
296 sizeof(find_tests)/sizeof(struct find_s));
298 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
300 /* Haystack text */
301 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
302 sizeof(find_tests2)/sizeof(struct find_s));
304 /* Setting a format on an arbitrary range should have no effect in search
305 results. This tests correct offset reporting across runs. */
306 cf2.cbSize = sizeof(CHARFORMAT2);
307 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
308 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
309 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
310 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
311 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
313 /* Haystack text, again */
314 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
315 sizeof(find_tests2)/sizeof(struct find_s));
317 /* Yet another range */
318 cf2.dwMask = CFM_BOLD | cf2.dwMask;
319 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
320 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
321 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
323 /* Haystack text, again */
324 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
325 sizeof(find_tests2)/sizeof(struct find_s));
327 DestroyWindow(hwndRichEdit);
330 static const struct getline_s {
331 int line;
332 size_t buffer_len;
333 const char *text;
334 } gl[] = {
335 {0, 10, "foo bar\r"},
336 {1, 10, "\r"},
337 {2, 10, "bar\r"},
338 {3, 10, "\r"},
340 /* Buffer smaller than line length */
341 {0, 2, "foo bar\r"},
342 {0, 1, "foo bar\r"},
343 {0, 0, "foo bar\r"}
346 static void test_EM_GETLINE(void)
348 int i;
349 HWND hwndRichEdit = new_richedit(NULL);
350 static const int nBuf = 1024;
351 char dest[1024], origdest[1024];
352 const char text[] = "foo bar\n"
353 "\n"
354 "bar\n";
356 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
358 memset(origdest, 0xBB, nBuf);
359 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
361 int nCopied;
362 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
363 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
364 memset(dest, 0xBB, nBuf);
365 *(WORD *) dest = gl[i].buffer_len;
367 /* EM_GETLINE appends a "\r\0" to the end of the line
368 * nCopied counts up to and including the '\r' */
369 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
370 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
371 expected_nCopied);
372 /* two special cases since a parameter is passed via dest */
373 if (gl[i].buffer_len == 0)
374 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
375 "buffer_len=0\n");
376 else if (gl[i].buffer_len == 1)
377 ok(dest[0] == gl[i].text[0] && !dest[1] &&
378 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
379 else
381 /* Prepare hex strings of buffers to dump on failure. */
382 char expectedbuf[1024];
383 char resultbuf[1024];
384 int j;
385 resultbuf[0] = '\0';
386 for (j = 0; j < 32; j++)
387 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
388 expectedbuf[0] = '\0';
389 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
390 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
391 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
392 sprintf(expectedbuf+strlen(expectedbuf), "??");
393 for (; j < 32; j++) /* Bytes after declared buffer size */
394 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
396 /* Test the part of the buffer that is expected to be written according
397 * to the MSDN documentation fo EM_GETLINE, which does not state that
398 * a NULL terminating character will be added unless no text is copied.
400 * Windows NT does not append a NULL terminating character, but
401 * Windows 2000 and up do append a NULL terminating character if there
402 * is space in the buffer. The test will ignore this difference. */
403 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
404 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
405 i, expected_bytes_written, expectedbuf, resultbuf);
406 /* Test the part of the buffer after the declared length to make sure
407 * there are no buffer overruns. */
408 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
409 nBuf - gl[i].buffer_len),
410 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
411 i, expected_bytes_written, expectedbuf, resultbuf);
415 DestroyWindow(hwndRichEdit);
418 static void test_EM_LINELENGTH(void)
420 HWND hwndRichEdit = new_richedit(NULL);
421 const char * text =
422 "richedit1\r"
423 "richedit1\n"
424 "richedit1\r\n"
425 "richedit1";
426 int offset_test[10][2] = {
427 {0, 9},
428 {5, 9},
429 {10, 9},
430 {15, 9},
431 {20, 9},
432 {25, 9},
433 {30, 9},
434 {35, 9},
435 {40, 0},
436 {45, 0},
438 int i;
439 LRESULT result;
441 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
443 for (i = 0; i < 10; i++) {
444 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
445 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
446 offset_test[i][0], result, offset_test[i][1]);
449 DestroyWindow(hwndRichEdit);
452 static int get_scroll_pos_y(HWND hwnd)
454 POINT p = {-1, -1};
455 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
456 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
457 return p.y;
460 static void move_cursor(HWND hwnd, LONG charindex)
462 CHARRANGE cr;
463 cr.cpMax = charindex;
464 cr.cpMin = charindex;
465 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
468 static void line_scroll(HWND hwnd, int amount)
470 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
473 static void test_EM_SCROLLCARET(void)
475 int prevY, curY;
476 const char text[] = "aa\n"
477 "this is a long line of text that should be longer than the "
478 "control's width\n"
479 "cc\n"
480 "dd\n"
481 "ee\n"
482 "ff\n"
483 "gg\n"
484 "hh\n";
485 /* The richedit window height needs to be large enough vertically to fit in
486 * more than two lines of text, so the new_richedit function can't be used
487 * since a height of 60 was not large enough on some systems.
489 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
490 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
491 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
492 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
494 /* Can't verify this */
495 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
497 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
499 /* Caret above visible window */
500 line_scroll(hwndRichEdit, 3);
501 prevY = get_scroll_pos_y(hwndRichEdit);
502 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
503 curY = get_scroll_pos_y(hwndRichEdit);
504 ok(prevY != curY, "%d == %d\n", prevY, curY);
506 /* Caret below visible window */
507 move_cursor(hwndRichEdit, sizeof(text) - 1);
508 line_scroll(hwndRichEdit, -3);
509 prevY = get_scroll_pos_y(hwndRichEdit);
510 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
511 curY = get_scroll_pos_y(hwndRichEdit);
512 ok(prevY != curY, "%d == %d\n", prevY, curY);
514 /* Caret in visible window */
515 move_cursor(hwndRichEdit, sizeof(text) - 2);
516 prevY = get_scroll_pos_y(hwndRichEdit);
517 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
518 curY = get_scroll_pos_y(hwndRichEdit);
519 ok(prevY == curY, "%d != %d\n", prevY, curY);
521 /* Caret still in visible window */
522 line_scroll(hwndRichEdit, -1);
523 prevY = get_scroll_pos_y(hwndRichEdit);
524 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
525 curY = get_scroll_pos_y(hwndRichEdit);
526 ok(prevY == curY, "%d != %d\n", prevY, curY);
528 DestroyWindow(hwndRichEdit);
531 static void test_EM_POSFROMCHAR(void)
533 HWND hwndRichEdit = new_richedit(NULL);
534 int i, expected;
535 LRESULT result;
536 unsigned int height = 0;
537 int xpos = 0;
538 POINTL pt;
539 LOCALESIGNATURE sig;
540 BOOL rtl;
541 static const char text[] = "aa\n"
542 "this is a long line of text that should be longer than the "
543 "control's width\n"
544 "cc\n"
545 "dd\n"
546 "ee\n"
547 "ff\n"
548 "gg\n"
549 "hh\n";
551 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
552 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
553 (sig.lsUsb[3] & 0x08000000) != 0);
555 /* Fill the control to lines to ensure that most of them are offscreen */
556 for (i = 0; i < 50; i++)
558 /* Do not modify the string; it is exactly 16 characters long. */
559 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
560 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
564 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
565 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
566 Richedit 3.0 accepts either of the above API conventions.
569 /* Testing Richedit 2.0 API format */
571 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
572 Since all lines are identical and drawn with the same font,
573 they should have the same height... right?
575 for (i = 0; i < 50; i++)
577 /* All the lines are 16 characters long */
578 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
579 if (i == 0)
581 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
582 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
583 xpos = LOWORD(result);
585 else if (i == 1)
587 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
588 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
589 height = HIWORD(result);
591 else
593 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
594 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
598 /* Testing position at end of text */
599 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
600 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
601 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
603 /* Testing position way past end of text */
604 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
605 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
606 expected = (rtl ? 8 : 1);
607 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
609 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
610 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
611 for (i = 0; i < 50; i++)
613 /* All the lines are 16 characters long */
614 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
615 ok((signed short)(HIWORD(result)) == (i - 1) * height,
616 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
617 (signed short)(HIWORD(result)), (i - 1) * height);
618 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
621 /* Testing position at end of text */
622 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
623 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
624 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
626 /* Testing position way past end of text */
627 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
628 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
629 expected = (rtl ? 8 : 1);
630 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
632 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
633 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
634 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
636 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
637 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
638 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
639 xpos = LOWORD(result);
641 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
642 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
643 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
644 ok((signed short)(LOWORD(result)) < xpos,
645 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
646 (signed short)(LOWORD(result)), xpos);
647 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
649 /* Test around end of text that doesn't end in a newline. */
650 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
651 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
652 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
653 ok(pt.x > 1, "pt.x = %d\n", pt.x);
654 xpos = pt.x;
655 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
656 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
657 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
658 xpos = (rtl ? pt.x + 7 : pt.x);
659 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
660 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
661 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
663 /* Try a negative position. */
664 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
665 ok(pt.x == 1, "pt.x = %d\n", pt.x);
667 DestroyWindow(hwndRichEdit);
670 static void test_EM_SETCHARFORMAT(void)
672 HWND hwndRichEdit = new_richedit(NULL);
673 CHARFORMAT2 cf2;
674 int rc = 0;
675 int tested_effects[] = {
676 CFE_BOLD,
677 CFE_ITALIC,
678 CFE_UNDERLINE,
679 CFE_STRIKEOUT,
680 CFE_PROTECTED,
681 CFE_LINK,
682 CFE_SUBSCRIPT,
683 CFE_SUPERSCRIPT,
686 int i;
687 CHARRANGE cr;
688 LOCALESIGNATURE sig;
689 BOOL rtl;
691 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
692 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
693 (sig.lsUsb[3] & 0x08000000) != 0);
695 /* Invalid flags, CHARFORMAT2 structure blanked out */
696 memset(&cf2, 0, sizeof(cf2));
697 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
698 (LPARAM) &cf2);
699 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
701 /* A valid flag, CHARFORMAT2 structure blanked out */
702 memset(&cf2, 0, sizeof(cf2));
703 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
704 (LPARAM) &cf2);
705 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
707 /* A valid flag, CHARFORMAT2 structure blanked out */
708 memset(&cf2, 0, sizeof(cf2));
709 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
710 (LPARAM) &cf2);
711 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
713 /* A valid flag, CHARFORMAT2 structure blanked out */
714 memset(&cf2, 0, sizeof(cf2));
715 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
716 (LPARAM) &cf2);
717 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
719 /* A valid flag, CHARFORMAT2 structure blanked out */
720 memset(&cf2, 0, sizeof(cf2));
721 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
722 (LPARAM) &cf2);
723 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
725 /* Invalid flags, CHARFORMAT2 structure minimally filled */
726 memset(&cf2, 0, sizeof(cf2));
727 cf2.cbSize = sizeof(CHARFORMAT2);
728 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
729 (LPARAM) &cf2);
730 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
731 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
732 ok(rc == FALSE, "Should not be able to undo here.\n");
733 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
735 /* A valid flag, CHARFORMAT2 structure minimally filled */
736 memset(&cf2, 0, sizeof(cf2));
737 cf2.cbSize = sizeof(CHARFORMAT2);
738 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
739 (LPARAM) &cf2);
740 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
741 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
742 ok(rc == FALSE, "Should not be able to undo here.\n");
743 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
745 /* A valid flag, CHARFORMAT2 structure minimally filled */
746 memset(&cf2, 0, sizeof(cf2));
747 cf2.cbSize = sizeof(CHARFORMAT2);
748 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
749 (LPARAM) &cf2);
750 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
751 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
752 ok(rc == FALSE, "Should not be able to undo here.\n");
753 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
755 /* A valid flag, CHARFORMAT2 structure minimally filled */
756 memset(&cf2, 0, sizeof(cf2));
757 cf2.cbSize = sizeof(CHARFORMAT2);
758 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
759 (LPARAM) &cf2);
760 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
761 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
762 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
763 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
765 /* A valid flag, CHARFORMAT2 structure minimally filled */
766 memset(&cf2, 0, sizeof(cf2));
767 cf2.cbSize = sizeof(CHARFORMAT2);
768 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
769 (LPARAM) &cf2);
770 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
771 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
772 ok(rc == TRUE, "Should not be able to undo here.\n");
773 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
775 cf2.cbSize = sizeof(CHARFORMAT2);
776 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
777 (LPARAM) &cf2);
779 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
780 cf2.cbSize = sizeof(CHARFORMAT2);
781 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
782 (LPARAM) &cf2);
783 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
784 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
786 /* wParam==0 is default char format, does not set modify */
787 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
788 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
789 ok(rc == 0, "Text marked as modified, expected not modified!\n");
790 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
791 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
792 if (! rtl)
794 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
795 ok(rc == 0, "Text marked as modified, expected not modified!\n");
797 else
798 skip("RTL language found\n");
800 /* wParam==SCF_SELECTION sets modify if nonempty selection */
801 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
802 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
803 ok(rc == 0, "Text marked as modified, expected not modified!\n");
804 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
805 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
806 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
807 ok(rc == 0, "Text marked as modified, expected not modified!\n");
809 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
810 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
811 ok(rc == 0, "Text marked as modified, expected not modified!\n");
812 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
813 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
814 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
815 ok(rc == 0, "Text marked as modified, expected not modified!\n");
816 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
817 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
818 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
819 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
820 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
822 /* wParam==SCF_ALL sets modify regardless of whether text is present */
823 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
824 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
825 ok(rc == 0, "Text marked as modified, expected not modified!\n");
826 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
827 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
828 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
829 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
831 DestroyWindow(hwndRichEdit);
833 /* EM_GETCHARFORMAT tests */
834 for (i = 0; tested_effects[i]; i++)
836 hwndRichEdit = new_richedit(NULL);
837 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
839 /* Need to set a TrueType font to get consistent CFM_BOLD results */
840 memset(&cf2, 0, sizeof(CHARFORMAT2));
841 cf2.cbSize = sizeof(CHARFORMAT2);
842 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
843 cf2.dwEffects = 0;
844 strcpy(cf2.szFaceName, "Courier New");
845 cf2.wWeight = FW_DONTCARE;
846 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
848 memset(&cf2, 0, sizeof(CHARFORMAT2));
849 cf2.cbSize = sizeof(CHARFORMAT2);
850 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
851 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
852 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
853 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
855 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
856 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
857 ok((cf2.dwEffects & tested_effects[i]) == 0,
858 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
860 memset(&cf2, 0, sizeof(CHARFORMAT2));
861 cf2.cbSize = sizeof(CHARFORMAT2);
862 cf2.dwMask = tested_effects[i];
863 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
864 cf2.dwMask = CFM_SUPERSCRIPT;
865 cf2.dwEffects = tested_effects[i];
866 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
867 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
869 memset(&cf2, 0, sizeof(CHARFORMAT2));
870 cf2.cbSize = sizeof(CHARFORMAT2);
871 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
872 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
873 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
874 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
876 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
877 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
878 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
879 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
881 memset(&cf2, 0, sizeof(CHARFORMAT2));
882 cf2.cbSize = sizeof(CHARFORMAT2);
883 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
884 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
885 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
886 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
888 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
889 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
890 ok((cf2.dwEffects & tested_effects[i]) == 0,
891 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
893 memset(&cf2, 0, sizeof(CHARFORMAT2));
894 cf2.cbSize = sizeof(CHARFORMAT2);
895 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
896 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
897 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
898 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
900 (cf2.dwMask & tested_effects[i]) == 0),
901 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
903 DestroyWindow(hwndRichEdit);
906 for (i = 0; tested_effects[i]; i++)
908 hwndRichEdit = new_richedit(NULL);
909 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
911 /* Need to set a TrueType font to get consistent CFM_BOLD results */
912 memset(&cf2, 0, sizeof(CHARFORMAT2));
913 cf2.cbSize = sizeof(CHARFORMAT2);
914 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
915 cf2.dwEffects = 0;
916 strcpy(cf2.szFaceName, "Courier New");
917 cf2.wWeight = FW_DONTCARE;
918 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
920 memset(&cf2, 0, sizeof(CHARFORMAT2));
921 cf2.cbSize = sizeof(CHARFORMAT2);
922 cf2.dwMask = tested_effects[i];
923 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
924 cf2.dwMask = CFM_SUPERSCRIPT;
925 cf2.dwEffects = tested_effects[i];
926 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
927 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
929 memset(&cf2, 0, sizeof(CHARFORMAT2));
930 cf2.cbSize = sizeof(CHARFORMAT2);
931 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
932 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
933 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
934 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
936 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
937 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
938 ok((cf2.dwEffects & tested_effects[i]) == 0,
939 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
941 memset(&cf2, 0, sizeof(CHARFORMAT2));
942 cf2.cbSize = sizeof(CHARFORMAT2);
943 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
944 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
945 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
946 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
948 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
949 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
950 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
951 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
953 memset(&cf2, 0, sizeof(CHARFORMAT2));
954 cf2.cbSize = sizeof(CHARFORMAT2);
955 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
956 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
957 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
958 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
960 (cf2.dwMask & tested_effects[i]) == 0),
961 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
962 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
963 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
965 DestroyWindow(hwndRichEdit);
968 /* Effects applied on an empty selection should take effect when selection is
969 replaced with text */
970 hwndRichEdit = new_richedit(NULL);
971 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
972 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
974 memset(&cf2, 0, sizeof(CHARFORMAT2));
975 cf2.cbSize = sizeof(CHARFORMAT2);
976 cf2.dwMask = CFM_BOLD;
977 cf2.dwEffects = CFE_BOLD;
978 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
980 /* Selection is now nonempty */
981 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
983 memset(&cf2, 0, sizeof(CHARFORMAT2));
984 cf2.cbSize = sizeof(CHARFORMAT2);
985 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
986 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
988 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
989 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
990 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
991 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
994 /* Set two effects on an empty selection */
995 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
996 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
998 memset(&cf2, 0, sizeof(CHARFORMAT2));
999 cf2.cbSize = sizeof(CHARFORMAT2);
1000 cf2.dwMask = CFM_BOLD;
1001 cf2.dwEffects = CFE_BOLD;
1002 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1003 cf2.dwMask = CFM_ITALIC;
1004 cf2.dwEffects = CFE_ITALIC;
1005 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1007 /* Selection is now nonempty */
1008 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1010 memset(&cf2, 0, sizeof(CHARFORMAT2));
1011 cf2.cbSize = sizeof(CHARFORMAT2);
1012 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1013 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1015 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1016 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1017 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1018 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1020 /* Setting the (empty) selection to exactly the same place as before should
1021 NOT clear the insertion style! */
1022 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1023 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1025 memset(&cf2, 0, sizeof(CHARFORMAT2));
1026 cf2.cbSize = sizeof(CHARFORMAT2);
1027 cf2.dwMask = CFM_BOLD;
1028 cf2.dwEffects = CFE_BOLD;
1029 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1031 /* Empty selection in same place, insert style should NOT be forgotten here. */
1032 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1034 /* Selection is now nonempty */
1035 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1037 memset(&cf2, 0, sizeof(CHARFORMAT2));
1038 cf2.cbSize = sizeof(CHARFORMAT2);
1039 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1040 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1042 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1043 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1044 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1045 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1047 /* Ditto with EM_EXSETSEL */
1048 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1049 cr.cpMin = 2; cr.cpMax = 2;
1050 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1052 memset(&cf2, 0, sizeof(CHARFORMAT2));
1053 cf2.cbSize = sizeof(CHARFORMAT2);
1054 cf2.dwMask = CFM_BOLD;
1055 cf2.dwEffects = CFE_BOLD;
1056 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1058 /* Empty selection in same place, insert style should NOT be forgotten here. */
1059 cr.cpMin = 2; cr.cpMax = 2;
1060 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1062 /* Selection is now nonempty */
1063 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1065 memset(&cf2, 0, sizeof(CHARFORMAT2));
1066 cf2.cbSize = sizeof(CHARFORMAT2);
1067 cr.cpMin = 2; cr.cpMax = 6;
1068 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1069 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1071 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1072 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1073 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1074 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1076 DestroyWindow(hwndRichEdit);
1079 static void test_EM_SETTEXTMODE(void)
1081 HWND hwndRichEdit = new_richedit(NULL);
1082 CHARFORMAT2 cf2, cf2test;
1083 CHARRANGE cr;
1084 int rc = 0;
1086 /*Attempt to use mutually exclusive modes*/
1087 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT|TM_RICHTEXT, 0);
1088 ok(rc == E_INVALIDARG,
1089 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1091 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1092 /*Insert text into the control*/
1094 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1096 /*Attempt to change the control to plain text mode*/
1097 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1098 ok(rc == E_UNEXPECTED,
1099 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1101 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1102 If rich text is pasted, it should have the same formatting as the rest
1103 of the text in the control*/
1105 /*Italicize the text
1106 *NOTE: If the default text was already italicized, the test will simply
1107 reverse; in other words, it will copy a regular "wine" into a plain
1108 text window that uses an italicized format*/
1109 cf2.cbSize = sizeof(CHARFORMAT2);
1110 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1111 (LPARAM) &cf2);
1113 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1114 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1116 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1117 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1119 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1120 however, SCF_ALL has been implemented*/
1121 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1122 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1124 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1125 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1127 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1129 /*Select the string "wine"*/
1130 cr.cpMin = 0;
1131 cr.cpMax = 4;
1132 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1134 /*Copy the italicized "wine" to the clipboard*/
1135 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1137 /*Reset the formatting to default*/
1138 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1139 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1140 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1142 /*Clear the text in the control*/
1143 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1145 /*Switch to Plain Text Mode*/
1146 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1147 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1149 /*Input "wine" again in normal format*/
1150 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1152 /*Paste the italicized "wine" into the control*/
1153 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1155 /*Select a character from the first "wine" string*/
1156 cr.cpMin = 2;
1157 cr.cpMax = 3;
1158 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1160 /*Retrieve its formatting*/
1161 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1162 (LPARAM) &cf2);
1164 /*Select a character from the second "wine" string*/
1165 cr.cpMin = 5;
1166 cr.cpMax = 6;
1167 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1169 /*Retrieve its formatting*/
1170 cf2test.cbSize = sizeof(CHARFORMAT2);
1171 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1172 (LPARAM) &cf2test);
1174 /*Compare the two formattings*/
1175 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1176 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1177 cf2.dwEffects, cf2test.dwEffects);
1178 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1179 printing "wine" in the current format(normal)
1180 pasting "wine" from the clipboard(italicized)
1181 comparing the two formats(should differ)*/
1183 /*Attempt to switch with text in control*/
1184 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1185 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1187 /*Clear control*/
1188 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1190 /*Switch into Rich Text mode*/
1191 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1192 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1194 /*Print "wine" in normal formatting into the control*/
1195 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1197 /*Paste italicized "wine" into the control*/
1198 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1200 /*Select text from the first "wine" string*/
1201 cr.cpMin = 1;
1202 cr.cpMax = 3;
1203 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1205 /*Retrieve its formatting*/
1206 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1207 (LPARAM) &cf2);
1209 /*Select text from the second "wine" string*/
1210 cr.cpMin = 6;
1211 cr.cpMax = 7;
1212 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1214 /*Retrieve its formatting*/
1215 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1216 (LPARAM) &cf2test);
1218 /*Test that the two formattings are not the same*/
1219 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1220 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1221 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1223 DestroyWindow(hwndRichEdit);
1226 static void test_SETPARAFORMAT(void)
1228 HWND hwndRichEdit = new_richedit(NULL);
1229 PARAFORMAT2 fmt;
1230 HRESULT ret;
1231 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1232 fmt.cbSize = sizeof(PARAFORMAT2);
1233 fmt.dwMask = PFM_ALIGNMENT;
1234 fmt.wAlignment = PFA_LEFT;
1236 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1237 ok(ret != 0, "expected non-zero got %d\n", ret);
1239 fmt.cbSize = sizeof(PARAFORMAT2);
1240 fmt.dwMask = -1;
1241 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1242 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1243 * between richedit different native builds of riched20.dll
1244 * used on different Windows versions. */
1245 ret &= ~PFM_TABLEROWDELIMITER;
1246 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1248 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1249 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1251 DestroyWindow(hwndRichEdit);
1254 static void test_TM_PLAINTEXT(void)
1256 /*Tests plain text properties*/
1258 HWND hwndRichEdit = new_richedit(NULL);
1259 CHARFORMAT2 cf2, cf2test;
1260 CHARRANGE cr;
1261 int rc = 0;
1263 /*Switch to plain text mode*/
1265 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1266 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1268 /*Fill control with text*/
1270 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1272 /*Select some text and bold it*/
1274 cr.cpMin = 10;
1275 cr.cpMax = 20;
1276 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1277 cf2.cbSize = sizeof(CHARFORMAT2);
1278 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1280 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1281 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1283 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1284 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1286 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1287 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1289 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1290 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1292 /*Get the formatting of those characters*/
1294 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1296 /*Get the formatting of some other characters*/
1297 cf2test.cbSize = sizeof(CHARFORMAT2);
1298 cr.cpMin = 21;
1299 cr.cpMax = 30;
1300 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1301 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1303 /*Test that they are the same as plain text allows only one formatting*/
1305 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1306 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1307 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1309 /*Fill the control with a "wine" string, which when inserted will be bold*/
1311 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1313 /*Copy the bolded "wine" string*/
1315 cr.cpMin = 0;
1316 cr.cpMax = 4;
1317 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1318 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1320 /*Swap back to rich text*/
1322 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1323 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1325 /*Set the default formatting to bold italics*/
1327 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1328 cf2.dwMask |= CFM_ITALIC;
1329 cf2.dwEffects ^= CFE_ITALIC;
1330 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1331 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1333 /*Set the text in the control to "wine", which will be bold and italicized*/
1335 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1337 /*Paste the plain text "wine" string, which should take the insert
1338 formatting, which at the moment is bold italics*/
1340 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1342 /*Select the first "wine" string and retrieve its formatting*/
1344 cr.cpMin = 1;
1345 cr.cpMax = 3;
1346 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1347 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1349 /*Select the second "wine" string and retrieve its formatting*/
1351 cr.cpMin = 5;
1352 cr.cpMax = 7;
1353 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1354 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1356 /*Compare the two formattings. They should be the same.*/
1358 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1359 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1360 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1361 DestroyWindow(hwndRichEdit);
1364 static void test_WM_GETTEXT(void)
1366 HWND hwndRichEdit = new_richedit(NULL);
1367 static const char text[] = "Hello. My name is RichEdit!";
1368 static const char text2[] = "Hello. My name is RichEdit!\r";
1369 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1370 char buffer[1024] = {0};
1371 int result;
1373 /* Baseline test with normal-sized buffer */
1374 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1375 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1376 ok(result == lstrlen(buffer),
1377 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1378 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1379 result = strcmp(buffer,text);
1380 ok(result == 0,
1381 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1383 /* Test for returned value of WM_GETTEXTLENGTH */
1384 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1385 ok(result == lstrlen(text),
1386 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1387 result, lstrlen(text));
1389 /* Test for behavior in overflow case */
1390 memset(buffer, 0, 1024);
1391 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1392 ok(result == 0 ||
1393 result == lstrlenA(text) - 1, /* XP, win2k3 */
1394 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1395 result = strcmp(buffer,text);
1396 if (result)
1397 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1398 ok(result == 0,
1399 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1401 /* Baseline test with normal-sized buffer and carriage return */
1402 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1403 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1404 ok(result == lstrlen(buffer),
1405 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1406 result = strcmp(buffer,text2_after);
1407 ok(result == 0,
1408 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1410 /* Test for returned value of WM_GETTEXTLENGTH */
1411 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1412 ok(result == lstrlen(text2_after),
1413 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1414 result, lstrlen(text2_after));
1416 /* Test for behavior of CRLF conversion in case of overflow */
1417 memset(buffer, 0, 1024);
1418 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1419 ok(result == 0 ||
1420 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1421 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1422 result = strcmp(buffer,text2);
1423 if (result)
1424 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1425 ok(result == 0,
1426 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1428 DestroyWindow(hwndRichEdit);
1431 static void test_EM_GETTEXTRANGE(void)
1433 HWND hwndRichEdit = new_richedit(NULL);
1434 const char * text1 = "foo bar\r\nfoo bar";
1435 const char * text2 = "foo bar\rfoo bar";
1436 const char * expect = "bar\rfoo";
1437 char buffer[1024] = {0};
1438 LRESULT result;
1439 TEXTRANGEA textRange;
1441 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1443 textRange.lpstrText = buffer;
1444 textRange.chrg.cpMin = 4;
1445 textRange.chrg.cpMax = 11;
1446 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1447 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1448 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1450 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1452 textRange.lpstrText = buffer;
1453 textRange.chrg.cpMin = 4;
1454 textRange.chrg.cpMax = 11;
1455 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1456 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1457 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1459 /* cpMax of text length is used instead of -1 in this case */
1460 textRange.lpstrText = buffer;
1461 textRange.chrg.cpMin = 0;
1462 textRange.chrg.cpMax = -1;
1463 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1464 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1465 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1467 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1468 textRange.lpstrText = buffer;
1469 textRange.chrg.cpMin = -1;
1470 textRange.chrg.cpMax = 1;
1471 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1472 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1473 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1475 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1476 textRange.lpstrText = buffer;
1477 textRange.chrg.cpMin = 1;
1478 textRange.chrg.cpMax = -1;
1479 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1480 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1481 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1483 /* no end character is copied if cpMax - cpMin < 0 */
1484 textRange.lpstrText = buffer;
1485 textRange.chrg.cpMin = 5;
1486 textRange.chrg.cpMax = 5;
1487 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1488 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1489 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1491 /* cpMax of text length is used if cpMax > text length*/
1492 textRange.lpstrText = buffer;
1493 textRange.chrg.cpMin = 0;
1494 textRange.chrg.cpMax = 1000;
1495 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1496 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1497 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1499 DestroyWindow(hwndRichEdit);
1502 static void test_EM_GETSELTEXT(void)
1504 HWND hwndRichEdit = new_richedit(NULL);
1505 const char * text1 = "foo bar\r\nfoo bar";
1506 const char * text2 = "foo bar\rfoo bar";
1507 const char * expect = "bar\rfoo";
1508 char buffer[1024] = {0};
1509 LRESULT result;
1511 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1513 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1514 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1515 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1516 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1518 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1520 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1521 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1522 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1523 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1525 DestroyWindow(hwndRichEdit);
1528 /* FIXME: need to test unimplemented options and robustly test wparam */
1529 static void test_EM_SETOPTIONS(void)
1531 HWND hwndRichEdit;
1532 static const char text[] = "Hello. My name is RichEdit!";
1533 char buffer[1024] = {0};
1534 DWORD dwStyle, options, oldOptions;
1535 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1536 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1537 ES_SELECTIONBAR|ES_VERTICAL;
1539 /* Test initial options. */
1540 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1541 0, 0, 200, 60, NULL, NULL,
1542 hmoduleRichEdit, NULL);
1543 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1544 RICHEDIT_CLASS, (int) GetLastError());
1545 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1546 ok(options == 0, "Incorrect initial options %x\n", options);
1547 DestroyWindow(hwndRichEdit);
1549 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1550 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1551 0, 0, 200, 60, NULL, NULL,
1552 hmoduleRichEdit, NULL);
1553 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1554 RICHEDIT_CLASS, (int) GetLastError());
1555 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1556 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1557 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1558 "Incorrect initial options %x\n", options);
1560 /* NEGATIVE TESTING - NO OPTIONS SET */
1561 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1562 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1564 /* testing no readonly by sending 'a' to the control*/
1565 SetFocus(hwndRichEdit);
1566 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1567 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1568 ok(buffer[0]=='a',
1569 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1570 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1572 /* READONLY - sending 'a' to the control */
1573 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1574 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1575 SetFocus(hwndRichEdit);
1576 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1577 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1578 ok(buffer[0]==text[0],
1579 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1581 /* EM_SETOPTIONS changes the window style, but changing the
1582 * window style does not change the options. */
1583 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1584 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1585 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1586 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1587 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1588 /* Confirm that the text is still read only. */
1589 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1590 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1591 ok(buffer[0]==text[0],
1592 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1594 oldOptions = options;
1595 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1596 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1597 ok(options == oldOptions,
1598 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1600 DestroyWindow(hwndRichEdit);
1603 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1605 CHARFORMAT2W text_format;
1606 text_format.cbSize = sizeof(text_format);
1607 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1608 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1609 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1612 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1614 int link_present = 0;
1616 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1617 if (is_url)
1618 { /* control text is url; should get CFE_LINK */
1619 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1621 else
1623 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1627 static HWND new_static_wnd(HWND parent) {
1628 return new_window("Static", 0, parent);
1631 static void test_EM_AUTOURLDETECT(void)
1633 /* DO NOT change the properties of the first two elements. To shorten the
1634 tests, all tests after WM_SETTEXT test just the first two elements -
1635 one non-URL and one URL */
1636 struct urls_s {
1637 const char *text;
1638 int is_url;
1639 } urls[12] = {
1640 {"winehq.org", 0},
1641 {"http://www.winehq.org", 1},
1642 {"http//winehq.org", 0},
1643 {"ww.winehq.org", 0},
1644 {"www.winehq.org", 1},
1645 {"ftp://192.168.1.1", 1},
1646 {"ftp//192.168.1.1", 0},
1647 {"mailto:your@email.com", 1},
1648 {"prospero:prosperoserver", 1},
1649 {"telnet:test", 1},
1650 {"news:newserver", 1},
1651 {"wais:waisserver", 1}
1654 int i, j;
1655 int urlRet=-1;
1656 HWND hwndRichEdit, parent;
1658 /* All of the following should cause the URL to be detected */
1659 const char * templates_delim[] = {
1660 "This is some text with X on it",
1661 "This is some text with (X) on it",
1662 "This is some text with X\r on it",
1663 "This is some text with ---X--- on it",
1664 "This is some text with \"X\" on it",
1665 "This is some text with 'X' on it",
1666 "This is some text with 'X' on it",
1667 "This is some text with :X: on it",
1669 "This text ends with X",
1671 "This is some text with X) on it",
1672 "This is some text with X--- on it",
1673 "This is some text with X\" on it",
1674 "This is some text with X' on it",
1675 "This is some text with X: on it",
1677 "This is some text with (X on it",
1678 "This is some text with \rX on it",
1679 "This is some text with ---X on it",
1680 "This is some text with \"X on it",
1681 "This is some text with 'X on it",
1682 "This is some text with :X on it",
1684 /* None of these should cause the URL to be detected */
1685 const char * templates_non_delim[] = {
1686 "This is some text with |X| on it",
1687 "This is some text with *X* on it",
1688 "This is some text with /X/ on it",
1689 "This is some text with +X+ on it",
1690 "This is some text with %X% on it",
1691 "This is some text with #X# on it",
1692 "This is some text with @X@ on it",
1693 "This is some text with \\X\\ on it",
1694 "This is some text with |X on it",
1695 "This is some text with *X on it",
1696 "This is some text with /X on it",
1697 "This is some text with +X on it",
1698 "This is some text with %X on it",
1699 "This is some text with #X on it",
1700 "This is some text with @X on it",
1701 "This is some text with \\X on it",
1703 /* All of these cause the URL detection to be extended by one more byte,
1704 thus demonstrating that the tested character is considered as part
1705 of the URL. */
1706 const char * templates_xten_delim[] = {
1707 "This is some text with X| on it",
1708 "This is some text with X* on it",
1709 "This is some text with X/ on it",
1710 "This is some text with X+ on it",
1711 "This is some text with X% on it",
1712 "This is some text with X# on it",
1713 "This is some text with X@ on it",
1714 "This is some text with X\\ on it",
1716 char buffer[1024];
1718 parent = new_static_wnd(NULL);
1719 hwndRichEdit = new_richedit(parent);
1720 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1721 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1722 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1723 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1724 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1725 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1726 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1727 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1728 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1729 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1730 /* for each url, check the text to see if CFE_LINK effect is present */
1731 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1733 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1734 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1735 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1737 /* Link detection should happen immediately upon WM_SETTEXT */
1738 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1740 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1742 DestroyWindow(hwndRichEdit);
1744 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1745 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1746 hwndRichEdit = new_richedit(parent);
1748 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1749 char * at_pos;
1750 int at_offset;
1751 int end_offset;
1753 at_pos = strchr(templates_delim[j], 'X');
1754 at_offset = at_pos - templates_delim[j];
1755 strncpy(buffer, templates_delim[j], at_offset);
1756 buffer[at_offset] = '\0';
1757 strcat(buffer, urls[i].text);
1758 strcat(buffer, templates_delim[j] + at_offset + 1);
1759 end_offset = at_offset + strlen(urls[i].text);
1761 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1762 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1764 /* This assumes no templates start with the URL itself, and that they
1765 have at least two characters before the URL text */
1766 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1767 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1768 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1769 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1770 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1771 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1773 if (urls[i].is_url)
1775 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1776 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1777 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1778 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1780 else
1782 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1783 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1784 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1785 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1787 if (buffer[end_offset] != '\0')
1789 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1790 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1791 if (buffer[end_offset +1] != '\0')
1793 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1794 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1799 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1800 char * at_pos;
1801 int at_offset;
1802 int end_offset;
1804 at_pos = strchr(templates_non_delim[j], 'X');
1805 at_offset = at_pos - templates_non_delim[j];
1806 strncpy(buffer, templates_non_delim[j], at_offset);
1807 buffer[at_offset] = '\0';
1808 strcat(buffer, urls[i].text);
1809 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1810 end_offset = at_offset + strlen(urls[i].text);
1812 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1813 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1815 /* This assumes no templates start with the URL itself, and that they
1816 have at least two characters before the URL text */
1817 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1818 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1819 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1820 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1821 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1822 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1824 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1825 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1826 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1827 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1828 if (buffer[end_offset] != '\0')
1830 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1831 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1832 if (buffer[end_offset +1] != '\0')
1834 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1835 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1840 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1841 char * at_pos;
1842 int at_offset;
1843 int end_offset;
1845 at_pos = strchr(templates_xten_delim[j], 'X');
1846 at_offset = at_pos - templates_xten_delim[j];
1847 strncpy(buffer, templates_xten_delim[j], at_offset);
1848 buffer[at_offset] = '\0';
1849 strcat(buffer, urls[i].text);
1850 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1851 end_offset = at_offset + strlen(urls[i].text);
1853 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1854 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1856 /* This assumes no templates start with the URL itself, and that they
1857 have at least two characters before the URL text */
1858 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1859 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1860 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1861 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1862 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1863 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1865 if (urls[i].is_url)
1867 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1868 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1869 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1870 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1871 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1872 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1874 else
1876 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1877 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1878 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1879 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1880 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1881 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1883 if (buffer[end_offset +1] != '\0')
1885 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1886 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1887 if (buffer[end_offset +2] != '\0')
1889 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1890 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1895 DestroyWindow(hwndRichEdit);
1896 hwndRichEdit = NULL;
1899 /* Test detection of URLs within normal text - WM_CHAR case. */
1900 /* Test only the first two URL examples for brevity */
1901 for (i = 0; i < 2; i++) {
1902 hwndRichEdit = new_richedit(parent);
1904 /* Also for brevity, test only the first three delimiters */
1905 for (j = 0; j < 3; j++) {
1906 char * at_pos;
1907 int at_offset;
1908 int end_offset;
1909 int u, v;
1911 at_pos = strchr(templates_delim[j], 'X');
1912 at_offset = at_pos - templates_delim[j];
1913 end_offset = at_offset + strlen(urls[i].text);
1915 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1916 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1917 for (u = 0; templates_delim[j][u]; u++) {
1918 if (templates_delim[j][u] == '\r') {
1919 simulate_typing_characters(hwndRichEdit, "\r");
1920 } else if (templates_delim[j][u] != 'X') {
1921 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1922 } else {
1923 for (v = 0; urls[i].text[v]; v++) {
1924 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1928 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1930 /* This assumes no templates start with the URL itself, and that they
1931 have at least two characters before the URL text */
1932 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1933 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1934 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1935 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1936 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1937 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1939 if (urls[i].is_url)
1941 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1942 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1943 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1944 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1946 else
1948 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1949 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1950 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1951 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1953 if (buffer[end_offset] != '\0')
1955 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1956 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1957 if (buffer[end_offset +1] != '\0')
1959 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1960 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1964 /* The following will insert a paragraph break after the first character
1965 of the URL candidate, thus breaking the URL. It is expected that the
1966 CFE_LINK attribute should break across both pieces of the URL */
1967 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1968 simulate_typing_characters(hwndRichEdit, "\r");
1969 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1971 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1972 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1973 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1974 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1975 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1976 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1978 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1979 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1980 /* end_offset moved because of paragraph break */
1981 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1982 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1983 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1984 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1986 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1987 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1988 if (buffer[end_offset +2] != '\0')
1990 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1991 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1995 /* The following will remove the just-inserted paragraph break, thus
1996 restoring the URL */
1997 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1998 simulate_typing_characters(hwndRichEdit, "\b");
1999 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2003 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2004 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2005 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2006 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2008 if (urls[i].is_url)
2010 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2011 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2012 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2013 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2015 else
2017 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2018 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2019 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2020 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2022 if (buffer[end_offset] != '\0')
2024 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2025 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2026 if (buffer[end_offset +1] != '\0')
2028 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2029 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2033 DestroyWindow(hwndRichEdit);
2034 hwndRichEdit = NULL;
2037 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2038 /* Test just the first two URL examples for brevity */
2039 for (i = 0; i < 2; i++) {
2040 SETTEXTEX st;
2042 hwndRichEdit = new_richedit(parent);
2044 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2045 be detected:
2046 1) Set entire text, a la WM_SETTEXT
2047 2) Set a selection of the text to the URL
2048 3) Set a portion of the text at a time, which eventually results in
2049 an URL
2050 All of them should give equivalent results
2053 /* Set entire text in one go, like WM_SETTEXT */
2054 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2055 char * at_pos;
2056 int at_offset;
2057 int end_offset;
2059 st.codepage = CP_ACP;
2060 st.flags = ST_DEFAULT;
2062 at_pos = strchr(templates_delim[j], 'X');
2063 at_offset = at_pos - templates_delim[j];
2064 strncpy(buffer, templates_delim[j], at_offset);
2065 buffer[at_offset] = '\0';
2066 strcat(buffer, urls[i].text);
2067 strcat(buffer, templates_delim[j] + at_offset + 1);
2068 end_offset = at_offset + strlen(urls[i].text);
2070 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2071 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2073 /* This assumes no templates start with the URL itself, and that they
2074 have at least two characters before the URL text */
2075 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2076 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2077 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2078 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2079 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2080 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2082 if (urls[i].is_url)
2084 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2085 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2086 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2087 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2089 else
2091 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2092 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2093 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2094 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2096 if (buffer[end_offset] != '\0')
2098 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2099 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2100 if (buffer[end_offset +1] != '\0')
2102 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2103 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2108 /* Set selection with X to the URL */
2109 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2110 char * at_pos;
2111 int at_offset;
2112 int end_offset;
2114 at_pos = strchr(templates_delim[j], 'X');
2115 at_offset = at_pos - templates_delim[j];
2116 end_offset = at_offset + strlen(urls[i].text);
2118 st.codepage = CP_ACP;
2119 st.flags = ST_DEFAULT;
2120 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2121 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2122 st.flags = ST_SELECTION;
2123 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2124 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2125 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2127 /* This assumes no templates start with the URL itself, and that they
2128 have at least two characters before the URL text */
2129 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2130 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2131 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2132 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2133 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2134 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2136 if (urls[i].is_url)
2138 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2139 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2140 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2141 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2143 else
2145 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2146 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2147 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2148 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2150 if (buffer[end_offset] != '\0')
2152 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2153 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2154 if (buffer[end_offset +1] != '\0')
2156 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2157 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2162 /* Set selection with X to the first character of the URL, then the rest */
2163 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2164 char * at_pos;
2165 int at_offset;
2166 int end_offset;
2168 at_pos = strchr(templates_delim[j], 'X');
2169 at_offset = at_pos - templates_delim[j];
2170 end_offset = at_offset + strlen(urls[i].text);
2172 strcpy(buffer, "YY");
2173 buffer[0] = urls[i].text[0];
2175 st.codepage = CP_ACP;
2176 st.flags = ST_DEFAULT;
2177 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2178 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2179 st.flags = ST_SELECTION;
2180 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2181 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2182 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2183 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2184 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2186 /* This assumes no templates start with the URL itself, and that they
2187 have at least two characters before the URL text */
2188 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2189 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2190 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2191 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2192 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2193 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2195 if (urls[i].is_url)
2197 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2198 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2199 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2200 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2202 else
2204 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2205 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2206 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2207 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2209 if (buffer[end_offset] != '\0')
2211 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2212 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2213 if (buffer[end_offset +1] != '\0')
2215 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2216 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2221 DestroyWindow(hwndRichEdit);
2222 hwndRichEdit = NULL;
2225 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2226 /* Test just the first two URL examples for brevity */
2227 for (i = 0; i < 2; i++) {
2228 hwndRichEdit = new_richedit(parent);
2230 /* Set selection with X to the URL */
2231 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2232 char * at_pos;
2233 int at_offset;
2234 int end_offset;
2236 at_pos = strchr(templates_delim[j], 'X');
2237 at_offset = at_pos - templates_delim[j];
2238 end_offset = at_offset + strlen(urls[i].text);
2240 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2241 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2242 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2243 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2244 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2246 /* This assumes no templates start with the URL itself, and that they
2247 have at least two characters before the URL text */
2248 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2249 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2250 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2251 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2252 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2253 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2255 if (urls[i].is_url)
2257 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2258 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2259 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2260 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2262 else
2264 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2265 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2266 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2267 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2269 if (buffer[end_offset] != '\0')
2271 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2272 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2273 if (buffer[end_offset +1] != '\0')
2275 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2276 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2281 /* Set selection with X to the first character of the URL, then the rest */
2282 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2283 char * at_pos;
2284 int at_offset;
2285 int end_offset;
2287 at_pos = strchr(templates_delim[j], 'X');
2288 at_offset = at_pos - templates_delim[j];
2289 end_offset = at_offset + strlen(urls[i].text);
2291 strcpy(buffer, "YY");
2292 buffer[0] = urls[i].text[0];
2294 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2295 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2296 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2297 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2298 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2299 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2300 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2302 /* This assumes no templates start with the URL itself, and that they
2303 have at least two characters before the URL text */
2304 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2305 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2306 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2307 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2308 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2309 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2311 if (urls[i].is_url)
2313 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2314 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2315 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2316 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, 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);
2325 if (buffer[end_offset] != '\0')
2327 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2328 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2329 if (buffer[end_offset +1] != '\0')
2331 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2332 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2337 DestroyWindow(hwndRichEdit);
2338 hwndRichEdit = NULL;
2341 DestroyWindow(parent);
2344 static void test_EM_SCROLL(void)
2346 int i, j;
2347 int r; /* return value */
2348 int expr; /* expected return value */
2349 HWND hwndRichEdit = new_richedit(NULL);
2350 int y_before, y_after; /* units of lines of text */
2352 /* test a richedit box containing a single line of text */
2353 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2354 expr = 0x00010000;
2355 for (i = 0; i < 4; i++) {
2356 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2358 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2359 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2360 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2361 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2362 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2363 "(i == %d)\n", y_after, i);
2367 * test a richedit box that will scroll. There are two general
2368 * cases: the case without any long lines and the case with a long
2369 * line.
2371 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2372 if (i == 0)
2373 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2374 else
2375 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2376 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2377 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2378 "LONG LINE \nb\nc\nd\ne");
2379 for (j = 0; j < 12; j++) /* reset scroll position to top */
2380 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2382 /* get first visible line */
2383 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2384 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2386 /* get new current first visible line */
2387 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2389 ok(((r & 0xffffff00) == 0x00010000) &&
2390 ((r & 0x000000ff) != 0x00000000),
2391 "EM_SCROLL page down didn't scroll by a small positive number of "
2392 "lines (r == 0x%08x)\n", r);
2393 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2394 "(line %d scrolled to line %d\n", y_before, y_after);
2396 y_before = y_after;
2398 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2399 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2400 ok(((r & 0xffffff00) == 0x0001ff00),
2401 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2402 "(r == 0x%08x)\n", r);
2403 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2404 "%d scrolled to line %d\n", y_before, y_after);
2406 y_before = y_after;
2408 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2410 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2412 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2413 "(r == 0x%08x)\n", r);
2414 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2415 "1 line (%d scrolled to %d)\n", y_before, y_after);
2417 y_before = y_after;
2419 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2421 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2423 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2424 "(r == 0x%08x)\n", r);
2425 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2426 "line (%d scrolled to %d)\n", y_before, y_after);
2428 y_before = y_after;
2430 r = SendMessage(hwndRichEdit, EM_SCROLL,
2431 SB_LINEUP, 0); /* lineup beyond top */
2433 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2435 ok(r == 0x00010000,
2436 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2437 ok(y_before == y_after,
2438 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2440 y_before = y_after;
2442 r = SendMessage(hwndRichEdit, EM_SCROLL,
2443 SB_PAGEUP, 0);/*page up beyond top */
2445 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2447 ok(r == 0x00010000,
2448 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2449 ok(y_before == y_after,
2450 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2452 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2453 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2454 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2455 r = SendMessage(hwndRichEdit, EM_SCROLL,
2456 SB_PAGEDOWN, 0); /* page down beyond bot */
2457 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2459 ok(r == 0x00010000,
2460 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2461 ok(y_before == y_after,
2462 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2463 y_before, y_after);
2465 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2466 SendMessage(hwndRichEdit, EM_SCROLL,
2467 SB_LINEDOWN, 0); /* line down beyond bot */
2468 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2470 ok(r == 0x00010000,
2471 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2472 ok(y_before == y_after,
2473 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2474 y_before, y_after);
2476 DestroyWindow(hwndRichEdit);
2479 static unsigned int recursionLevel = 0;
2480 static unsigned int WM_SIZE_recursionLevel = 0;
2481 static BOOL bailedOutOfRecursion = FALSE;
2482 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2484 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2486 LRESULT r;
2488 if (bailedOutOfRecursion) return 0;
2489 if (recursionLevel >= 32) {
2490 bailedOutOfRecursion = TRUE;
2491 return 0;
2494 recursionLevel++;
2495 switch (message) {
2496 case WM_SIZE:
2497 WM_SIZE_recursionLevel++;
2498 r = richeditProc(hwnd, message, wParam, lParam);
2499 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2500 ShowScrollBar(hwnd, SB_VERT, TRUE);
2501 WM_SIZE_recursionLevel--;
2502 break;
2503 default:
2504 r = richeditProc(hwnd, message, wParam, lParam);
2505 break;
2507 recursionLevel--;
2508 return r;
2511 static void test_scrollbar_visibility(void)
2513 HWND hwndRichEdit;
2514 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2515 SCROLLINFO si;
2516 WNDCLASSA cls;
2517 BOOL r;
2519 /* These tests show that richedit should temporarily refrain from automatically
2520 hiding or showing its scrollbars (vertical at least) when an explicit request
2521 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2522 Some applications depend on forced showing (when otherwise richedit would
2523 hide the vertical scrollbar) and are thrown on an endless recursive loop
2524 if richedit auto-hides the scrollbar again. Apparently they never heard of
2525 the ES_DISABLENOSCROLL style... */
2527 hwndRichEdit = new_richedit(NULL);
2529 /* Test default scrollbar visibility behavior */
2530 memset(&si, 0, sizeof(si));
2531 si.cbSize = sizeof(si);
2532 si.fMask = SIF_PAGE | SIF_RANGE;
2533 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2534 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2535 "Vertical scrollbar is visible, should be invisible.\n");
2536 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2537 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2538 si.nPage, si.nMin, si.nMax);
2540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2541 memset(&si, 0, sizeof(si));
2542 si.cbSize = sizeof(si);
2543 si.fMask = SIF_PAGE | SIF_RANGE;
2544 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2545 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2546 "Vertical scrollbar is visible, should be invisible.\n");
2547 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2548 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2549 si.nPage, si.nMin, si.nMax);
2551 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2552 memset(&si, 0, sizeof(si));
2553 si.cbSize = sizeof(si);
2554 si.fMask = SIF_PAGE | SIF_RANGE;
2555 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2556 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2557 "Vertical scrollbar is invisible, should be visible.\n");
2558 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2559 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2560 si.nPage, si.nMin, si.nMax);
2562 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2563 even though it hides the scrollbar */
2564 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2565 memset(&si, 0, sizeof(si));
2566 si.cbSize = sizeof(si);
2567 si.fMask = SIF_PAGE | SIF_RANGE;
2568 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2569 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2570 "Vertical scrollbar is visible, should be invisible.\n");
2571 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2572 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2573 si.nPage, si.nMin, si.nMax);
2575 /* Setting non-scrolling text again does *not* reset scrollbar range */
2576 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2577 memset(&si, 0, sizeof(si));
2578 si.cbSize = sizeof(si);
2579 si.fMask = SIF_PAGE | SIF_RANGE;
2580 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2581 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2582 "Vertical scrollbar is visible, should be invisible.\n");
2583 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2584 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2585 si.nPage, si.nMin, si.nMax);
2587 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2588 memset(&si, 0, sizeof(si));
2589 si.cbSize = sizeof(si);
2590 si.fMask = SIF_PAGE | SIF_RANGE;
2591 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2592 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2593 "Vertical scrollbar is visible, should be invisible.\n");
2594 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2595 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2596 si.nPage, si.nMin, si.nMax);
2598 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2599 memset(&si, 0, sizeof(si));
2600 si.cbSize = sizeof(si);
2601 si.fMask = SIF_PAGE | SIF_RANGE;
2602 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2603 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2604 "Vertical scrollbar is visible, should be invisible.\n");
2605 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2606 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2607 si.nPage, si.nMin, si.nMax);
2609 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2610 memset(&si, 0, sizeof(si));
2611 si.cbSize = sizeof(si);
2612 si.fMask = SIF_PAGE | SIF_RANGE;
2613 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2614 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2615 "Vertical scrollbar is visible, should be invisible.\n");
2616 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2617 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2618 si.nPage, si.nMin, si.nMax);
2620 DestroyWindow(hwndRichEdit);
2622 /* Test again, with ES_DISABLENOSCROLL style */
2623 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2625 /* Test default scrollbar visibility behavior */
2626 memset(&si, 0, sizeof(si));
2627 si.cbSize = sizeof(si);
2628 si.fMask = SIF_PAGE | SIF_RANGE;
2629 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2630 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2631 "Vertical scrollbar is invisible, should be visible.\n");
2632 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2633 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2634 si.nPage, si.nMin, si.nMax);
2636 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2637 memset(&si, 0, sizeof(si));
2638 si.cbSize = sizeof(si);
2639 si.fMask = SIF_PAGE | SIF_RANGE;
2640 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2641 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2642 "Vertical scrollbar is invisible, should be visible.\n");
2643 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2644 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2645 si.nPage, si.nMin, si.nMax);
2647 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2648 memset(&si, 0, sizeof(si));
2649 si.cbSize = sizeof(si);
2650 si.fMask = SIF_PAGE | SIF_RANGE;
2651 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2652 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2653 "Vertical scrollbar is invisible, should be visible.\n");
2654 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2655 "reported page/range is %d (%d..%d)\n",
2656 si.nPage, si.nMin, si.nMax);
2658 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2659 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2660 memset(&si, 0, sizeof(si));
2661 si.cbSize = sizeof(si);
2662 si.fMask = SIF_PAGE | SIF_RANGE;
2663 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2664 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2665 "Vertical scrollbar is invisible, should be visible.\n");
2666 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2667 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2668 si.nPage, si.nMin, si.nMax);
2670 /* Setting non-scrolling text again does *not* reset scrollbar range */
2671 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2672 memset(&si, 0, sizeof(si));
2673 si.cbSize = sizeof(si);
2674 si.fMask = SIF_PAGE | SIF_RANGE;
2675 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2676 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2677 "Vertical scrollbar is invisible, should be visible.\n");
2678 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2679 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2680 si.nPage, si.nMin, si.nMax);
2682 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2683 memset(&si, 0, sizeof(si));
2684 si.cbSize = sizeof(si);
2685 si.fMask = SIF_PAGE | SIF_RANGE;
2686 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2687 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2688 "Vertical scrollbar is invisible, should be visible.\n");
2689 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2690 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2691 si.nPage, si.nMin, si.nMax);
2693 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2694 memset(&si, 0, sizeof(si));
2695 si.cbSize = sizeof(si);
2696 si.fMask = SIF_PAGE | SIF_RANGE;
2697 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2698 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2699 "Vertical scrollbar is invisible, should be visible.\n");
2700 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2701 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2702 si.nPage, si.nMin, si.nMax);
2704 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2705 memset(&si, 0, sizeof(si));
2706 si.cbSize = sizeof(si);
2707 si.fMask = SIF_PAGE | SIF_RANGE;
2708 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2709 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2710 "Vertical scrollbar is invisible, should be visible.\n");
2711 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2712 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2713 si.nPage, si.nMin, si.nMax);
2715 DestroyWindow(hwndRichEdit);
2717 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2718 hwndRichEdit = new_richedit(NULL);
2720 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2721 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2722 memset(&si, 0, sizeof(si));
2723 si.cbSize = sizeof(si);
2724 si.fMask = SIF_PAGE | SIF_RANGE;
2725 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2726 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2727 "Vertical scrollbar is invisible, should be visible.\n");
2728 todo_wine {
2729 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2730 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2731 si.nPage, si.nMin, si.nMax);
2734 /* Ditto, see above */
2735 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2736 memset(&si, 0, sizeof(si));
2737 si.cbSize = sizeof(si);
2738 si.fMask = SIF_PAGE | SIF_RANGE;
2739 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2740 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2741 "Vertical scrollbar is invisible, should be visible.\n");
2742 todo_wine {
2743 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2744 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2745 si.nPage, si.nMin, si.nMax);
2748 /* Ditto, see above */
2749 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2750 memset(&si, 0, sizeof(si));
2751 si.cbSize = sizeof(si);
2752 si.fMask = SIF_PAGE | SIF_RANGE;
2753 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2754 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2755 "Vertical scrollbar is invisible, should be visible.\n");
2756 todo_wine {
2757 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2758 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2759 si.nPage, si.nMin, si.nMax);
2762 /* Ditto, see above */
2763 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2764 memset(&si, 0, sizeof(si));
2765 si.cbSize = sizeof(si);
2766 si.fMask = SIF_PAGE | SIF_RANGE;
2767 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2768 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2769 "Vertical scrollbar is invisible, should be visible.\n");
2770 todo_wine {
2771 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2772 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2773 si.nPage, si.nMin, si.nMax);
2776 /* Ditto, see above */
2777 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2778 memset(&si, 0, sizeof(si));
2779 si.cbSize = sizeof(si);
2780 si.fMask = SIF_PAGE | SIF_RANGE;
2781 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2782 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2783 "Vertical scrollbar is invisible, should be visible.\n");
2784 todo_wine {
2785 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2786 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2787 si.nPage, si.nMin, si.nMax);
2790 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2791 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2792 memset(&si, 0, sizeof(si));
2793 si.cbSize = sizeof(si);
2794 si.fMask = SIF_PAGE | SIF_RANGE;
2795 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2796 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2797 "Vertical scrollbar is visible, should be invisible.\n");
2798 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2799 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2800 si.nPage, si.nMin, si.nMax);
2802 DestroyWindow(hwndRichEdit);
2804 hwndRichEdit = new_richedit(NULL);
2806 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2807 memset(&si, 0, sizeof(si));
2808 si.cbSize = sizeof(si);
2809 si.fMask = SIF_PAGE | SIF_RANGE;
2810 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2811 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2812 "Vertical scrollbar is visible, should be invisible.\n");
2813 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2814 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2815 si.nPage, si.nMin, si.nMax);
2817 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2818 memset(&si, 0, sizeof(si));
2819 si.cbSize = sizeof(si);
2820 si.fMask = SIF_PAGE | SIF_RANGE;
2821 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2822 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2823 "Vertical scrollbar is visible, should be invisible.\n");
2824 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2825 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2826 si.nPage, si.nMin, si.nMax);
2828 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2829 memset(&si, 0, sizeof(si));
2830 si.cbSize = sizeof(si);
2831 si.fMask = SIF_PAGE | SIF_RANGE;
2832 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2833 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2834 "Vertical scrollbar is visible, should be invisible.\n");
2835 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2836 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2837 si.nPage, si.nMin, si.nMax);
2839 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2840 memset(&si, 0, sizeof(si));
2841 si.cbSize = sizeof(si);
2842 si.fMask = SIF_PAGE | SIF_RANGE;
2843 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2844 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2845 "Vertical scrollbar is visible, should be invisible.\n");
2846 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2847 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2848 si.nPage, si.nMin, si.nMax);
2850 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2851 memset(&si, 0, sizeof(si));
2852 si.cbSize = sizeof(si);
2853 si.fMask = SIF_PAGE | SIF_RANGE;
2854 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2855 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2856 "Vertical scrollbar is invisible, should be visible.\n");
2857 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2858 "reported page/range is %d (%d..%d)\n",
2859 si.nPage, si.nMin, si.nMax);
2861 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2862 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2863 memset(&si, 0, sizeof(si));
2864 si.cbSize = sizeof(si);
2865 si.fMask = SIF_PAGE | SIF_RANGE;
2866 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2867 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2868 "Vertical scrollbar is visible, should be invisible.\n");
2869 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2870 "reported page/range is %d (%d..%d)\n",
2871 si.nPage, si.nMin, si.nMax);
2873 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2874 memset(&si, 0, sizeof(si));
2875 si.cbSize = sizeof(si);
2876 si.fMask = SIF_PAGE | SIF_RANGE;
2877 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2878 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2879 "Vertical scrollbar is visible, should be invisible.\n");
2880 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2881 "reported page/range is %d (%d..%d)\n",
2882 si.nPage, si.nMin, si.nMax);
2884 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2885 EM_SCROLL will make visible any forcefully invisible scrollbar */
2886 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2887 memset(&si, 0, sizeof(si));
2888 si.cbSize = sizeof(si);
2889 si.fMask = SIF_PAGE | SIF_RANGE;
2890 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2891 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2892 "Vertical scrollbar is invisible, should be visible.\n");
2893 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2894 "reported page/range is %d (%d..%d)\n",
2895 si.nPage, si.nMin, si.nMax);
2897 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2898 memset(&si, 0, sizeof(si));
2899 si.cbSize = sizeof(si);
2900 si.fMask = SIF_PAGE | SIF_RANGE;
2901 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2902 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2903 "Vertical scrollbar is visible, should be invisible.\n");
2904 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2905 "reported page/range is %d (%d..%d)\n",
2906 si.nPage, si.nMin, si.nMax);
2908 /* Again, EM_SCROLL, with SB_LINEUP */
2909 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2910 memset(&si, 0, sizeof(si));
2911 si.cbSize = sizeof(si);
2912 si.fMask = SIF_PAGE | SIF_RANGE;
2913 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2914 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2915 "Vertical scrollbar is invisible, should be visible.\n");
2916 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2917 "reported page/range is %d (%d..%d)\n",
2918 si.nPage, si.nMin, si.nMax);
2920 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2921 memset(&si, 0, sizeof(si));
2922 si.cbSize = sizeof(si);
2923 si.fMask = SIF_PAGE | SIF_RANGE;
2924 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2925 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2926 "Vertical scrollbar is visible, should be invisible.\n");
2927 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2928 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2929 si.nPage, si.nMin, si.nMax);
2931 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2932 memset(&si, 0, sizeof(si));
2933 si.cbSize = sizeof(si);
2934 si.fMask = SIF_PAGE | SIF_RANGE;
2935 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2936 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2937 "Vertical scrollbar is invisible, should be visible.\n");
2938 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2939 "reported page/range is %d (%d..%d)\n",
2940 si.nPage, si.nMin, si.nMax);
2942 DestroyWindow(hwndRichEdit);
2945 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2946 hwndRichEdit = new_richedit(NULL);
2948 #define ENABLE_WS_VSCROLL(hwnd) \
2949 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2950 #define DISABLE_WS_VSCROLL(hwnd) \
2951 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2953 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2954 ENABLE_WS_VSCROLL(hwndRichEdit);
2955 memset(&si, 0, sizeof(si));
2956 si.cbSize = sizeof(si);
2957 si.fMask = SIF_PAGE | SIF_RANGE;
2958 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2959 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2960 "Vertical scrollbar is invisible, should be visible.\n");
2961 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2962 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2963 si.nPage, si.nMin, si.nMax);
2965 /* Ditto, see above */
2966 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2967 memset(&si, 0, sizeof(si));
2968 si.cbSize = sizeof(si);
2969 si.fMask = SIF_PAGE | SIF_RANGE;
2970 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2971 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2972 "Vertical scrollbar is invisible, should be visible.\n");
2973 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2974 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2975 si.nPage, si.nMin, si.nMax);
2977 /* Ditto, see above */
2978 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2979 memset(&si, 0, sizeof(si));
2980 si.cbSize = sizeof(si);
2981 si.fMask = SIF_PAGE | SIF_RANGE;
2982 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2983 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2984 "Vertical scrollbar is invisible, should be visible.\n");
2985 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2986 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2987 si.nPage, si.nMin, si.nMax);
2989 /* Ditto, see above */
2990 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2991 memset(&si, 0, sizeof(si));
2992 si.cbSize = sizeof(si);
2993 si.fMask = SIF_PAGE | SIF_RANGE;
2994 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2995 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2996 "Vertical scrollbar is invisible, should be visible.\n");
2997 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2998 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2999 si.nPage, si.nMin, si.nMax);
3001 /* Ditto, see above */
3002 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3003 memset(&si, 0, sizeof(si));
3004 si.cbSize = sizeof(si);
3005 si.fMask = SIF_PAGE | SIF_RANGE;
3006 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3007 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3008 "Vertical scrollbar is invisible, should be visible.\n");
3009 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3010 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3011 si.nPage, si.nMin, si.nMax);
3013 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3014 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3015 memset(&si, 0, sizeof(si));
3016 si.cbSize = sizeof(si);
3017 si.fMask = SIF_PAGE | SIF_RANGE;
3018 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3019 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3020 "Vertical scrollbar is visible, should be invisible.\n");
3021 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3022 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3023 si.nPage, si.nMin, si.nMax);
3025 DestroyWindow(hwndRichEdit);
3027 hwndRichEdit = new_richedit(NULL);
3029 DISABLE_WS_VSCROLL(hwndRichEdit);
3030 memset(&si, 0, sizeof(si));
3031 si.cbSize = sizeof(si);
3032 si.fMask = SIF_PAGE | SIF_RANGE;
3033 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3034 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3035 "Vertical scrollbar is visible, should be invisible.\n");
3036 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3037 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3038 si.nPage, si.nMin, si.nMax);
3040 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3041 memset(&si, 0, sizeof(si));
3042 si.cbSize = sizeof(si);
3043 si.fMask = SIF_PAGE | SIF_RANGE;
3044 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3045 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3046 "Vertical scrollbar is visible, should be invisible.\n");
3047 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3048 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3049 si.nPage, si.nMin, si.nMax);
3051 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3052 memset(&si, 0, sizeof(si));
3053 si.cbSize = sizeof(si);
3054 si.fMask = SIF_PAGE | SIF_RANGE;
3055 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3056 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3057 "Vertical scrollbar is visible, should be invisible.\n");
3058 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3059 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3060 si.nPage, si.nMin, si.nMax);
3062 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3063 memset(&si, 0, sizeof(si));
3064 si.cbSize = sizeof(si);
3065 si.fMask = SIF_PAGE | SIF_RANGE;
3066 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3067 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3068 "Vertical scrollbar is visible, should be invisible.\n");
3069 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3070 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3071 si.nPage, si.nMin, si.nMax);
3073 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3074 memset(&si, 0, sizeof(si));
3075 si.cbSize = sizeof(si);
3076 si.fMask = SIF_PAGE | SIF_RANGE;
3077 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3078 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3079 "Vertical scrollbar is invisible, should be visible.\n");
3080 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3081 "reported page/range is %d (%d..%d)\n",
3082 si.nPage, si.nMin, si.nMax);
3084 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3085 DISABLE_WS_VSCROLL(hwndRichEdit);
3086 memset(&si, 0, sizeof(si));
3087 si.cbSize = sizeof(si);
3088 si.fMask = SIF_PAGE | SIF_RANGE;
3089 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3090 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3091 "Vertical scrollbar is visible, should be invisible.\n");
3092 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3093 "reported page/range is %d (%d..%d)\n",
3094 si.nPage, si.nMin, si.nMax);
3096 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3097 memset(&si, 0, sizeof(si));
3098 si.cbSize = sizeof(si);
3099 si.fMask = SIF_PAGE | SIF_RANGE;
3100 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3101 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3102 "Vertical scrollbar is visible, should be invisible.\n");
3103 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3104 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3105 si.nPage, si.nMin, si.nMax);
3107 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3108 memset(&si, 0, sizeof(si));
3109 si.cbSize = sizeof(si);
3110 si.fMask = SIF_PAGE | SIF_RANGE;
3111 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3112 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3113 "Vertical scrollbar is invisible, should be visible.\n");
3114 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3115 "reported page/range is %d (%d..%d)\n",
3116 si.nPage, si.nMin, si.nMax);
3118 DISABLE_WS_VSCROLL(hwndRichEdit);
3119 memset(&si, 0, sizeof(si));
3120 si.cbSize = sizeof(si);
3121 si.fMask = SIF_PAGE | SIF_RANGE;
3122 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3123 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3124 "Vertical scrollbar is visible, should be invisible.\n");
3125 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3126 "reported page/range is %d (%d..%d)\n",
3127 si.nPage, si.nMin, si.nMax);
3129 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3130 EM_SCROLL will make visible any forcefully invisible scrollbar */
3131 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3132 memset(&si, 0, sizeof(si));
3133 si.cbSize = sizeof(si);
3134 si.fMask = SIF_PAGE | SIF_RANGE;
3135 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3136 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3137 "Vertical scrollbar is invisible, should be visible.\n");
3138 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3139 "reported page/range is %d (%d..%d)\n",
3140 si.nPage, si.nMin, si.nMax);
3142 DISABLE_WS_VSCROLL(hwndRichEdit);
3143 memset(&si, 0, sizeof(si));
3144 si.cbSize = sizeof(si);
3145 si.fMask = SIF_PAGE | SIF_RANGE;
3146 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3147 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3148 "Vertical scrollbar is visible, should be invisible.\n");
3149 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3150 "reported page/range is %d (%d..%d)\n",
3151 si.nPage, si.nMin, si.nMax);
3153 /* Again, EM_SCROLL, with SB_LINEUP */
3154 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3155 memset(&si, 0, sizeof(si));
3156 si.cbSize = sizeof(si);
3157 si.fMask = SIF_PAGE | SIF_RANGE;
3158 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3159 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3160 "Vertical scrollbar is invisible, should be visible.\n");
3161 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3162 "reported page/range is %d (%d..%d)\n",
3163 si.nPage, si.nMin, si.nMax);
3165 DestroyWindow(hwndRichEdit);
3167 /* This window proc models what is going on with Corman Lisp 3.0.
3168 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3169 force the scrollbar into visibility. Recursion should NOT happen
3170 as a result of this action.
3172 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3173 if (r) {
3174 richeditProc = cls.lpfnWndProc;
3175 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3176 cls.lpszClassName = "RicheditStupidOverride";
3177 if(!RegisterClassA(&cls)) assert(0);
3179 recursionLevel = 0;
3180 WM_SIZE_recursionLevel = 0;
3181 bailedOutOfRecursion = FALSE;
3182 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3183 ok(!bailedOutOfRecursion,
3184 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3186 recursionLevel = 0;
3187 WM_SIZE_recursionLevel = 0;
3188 bailedOutOfRecursion = FALSE;
3189 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3190 ok(!bailedOutOfRecursion,
3191 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3193 /* Unblock window in order to process WM_DESTROY */
3194 recursionLevel = 0;
3195 bailedOutOfRecursion = FALSE;
3196 WM_SIZE_recursionLevel = 0;
3197 DestroyWindow(hwndRichEdit);
3201 static void test_EM_SETUNDOLIMIT(void)
3203 /* cases we test for:
3204 * default behaviour - limiting at 100 undo's
3205 * undo disabled - setting a limit of 0
3206 * undo limited - undo limit set to some to some number, like 2
3207 * bad input - sending a negative number should default to 100 undo's */
3209 HWND hwndRichEdit = new_richedit(NULL);
3210 CHARRANGE cr;
3211 int i;
3212 int result;
3214 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3215 cr.cpMin = 0;
3216 cr.cpMax = 1;
3217 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3218 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3219 also, multiple pastes don't combine like WM_CHAR would */
3220 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3222 /* first case - check the default */
3223 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3224 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3225 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3226 for (i=0; i<100; i++) /* Undo 100 of them */
3227 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3228 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3229 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3231 /* second case - cannot undo */
3232 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3233 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3234 SendMessage(hwndRichEdit,
3235 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3236 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3237 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3239 /* third case - set it to an arbitrary number */
3240 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3241 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3242 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3243 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3244 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3245 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3246 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3247 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3248 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3249 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3250 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3251 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3252 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3253 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3255 /* fourth case - setting negative numbers should default to 100 undos */
3256 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3257 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3258 ok (result == 100,
3259 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3261 DestroyWindow(hwndRichEdit);
3264 static void test_ES_PASSWORD(void)
3266 /* This isn't hugely testable, so we're just going to run it through its paces */
3268 HWND hwndRichEdit = new_richedit(NULL);
3269 WCHAR result;
3271 /* First, check the default of a regular control */
3272 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3273 ok (result == 0,
3274 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3276 /* Now, set it to something normal */
3277 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3278 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3279 ok (result == 120,
3280 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3282 /* Now, set it to something odd */
3283 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3284 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3285 ok (result == 1234,
3286 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3287 DestroyWindow(hwndRichEdit);
3290 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3291 LPBYTE pbBuff,
3292 LONG cb,
3293 LONG *pcb)
3295 char** str = (char**)dwCookie;
3296 *pcb = cb;
3297 if (*pcb > 0) {
3298 memcpy(*str, pbBuff, *pcb);
3299 *str += *pcb;
3301 return 0;
3304 static void test_WM_SETTEXT(void)
3306 HWND hwndRichEdit = new_richedit(NULL);
3307 const char * TestItem1 = "TestSomeText";
3308 const char * TestItem2 = "TestSomeText\r";
3309 const char * TestItem2_after = "TestSomeText\r\n";
3310 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3311 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3312 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3313 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3314 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3315 const char * TestItem5_after = "TestSomeText TestSomeText";
3316 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3317 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3318 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3319 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3321 const char rtftextA[] = "{\\rtf sometext}";
3322 const char urtftextA[] = "{\\urtf sometext}";
3323 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3324 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3325 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3327 char buf[1024] = {0};
3328 WCHAR bufW[1024] = {0};
3329 LRESULT result;
3331 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3332 any solitary \r to be converted to \r\n on return. Properly paired
3333 \r\n are not affected. It also shows that the special sequence \r\r\n
3334 gets converted to a single space.
3337 #define TEST_SETTEXT(a, b) \
3338 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3339 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3340 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3341 ok (result == lstrlen(buf), \
3342 "WM_GETTEXT returned %ld instead of expected %u\n", \
3343 result, lstrlen(buf)); \
3344 result = strcmp(b, buf); \
3345 ok(result == 0, \
3346 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3348 TEST_SETTEXT(TestItem1, TestItem1)
3349 TEST_SETTEXT(TestItem2, TestItem2_after)
3350 TEST_SETTEXT(TestItem3, TestItem3_after)
3351 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3352 TEST_SETTEXT(TestItem4, TestItem4_after)
3353 TEST_SETTEXT(TestItem5, TestItem5_after)
3354 TEST_SETTEXT(TestItem6, TestItem6_after)
3355 TEST_SETTEXT(TestItem7, TestItem7_after)
3357 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3358 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3359 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3360 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3361 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3362 DestroyWindow(hwndRichEdit);
3363 #undef TEST_SETTEXT
3365 #define TEST_SETTEXTW(a, b) \
3366 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3367 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3368 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3369 ok (result == lstrlenW(bufW), \
3370 "WM_GETTEXT returned %ld instead of expected %u\n", \
3371 result, lstrlenW(bufW)); \
3372 result = lstrcmpW(b, bufW); \
3373 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3375 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3376 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3377 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3378 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3379 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3380 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3381 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3382 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3383 DestroyWindow(hwndRichEdit);
3384 #undef TEST_SETTEXTW
3387 static void test_EM_STREAMOUT(void)
3389 HWND hwndRichEdit = new_richedit(NULL);
3390 int r;
3391 EDITSTREAM es;
3392 char buf[1024] = {0};
3393 char * p;
3395 const char * TestItem1 = "TestSomeText";
3396 const char * TestItem2 = "TestSomeText\r";
3397 const char * TestItem3 = "TestSomeText\r\n";
3399 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3400 p = buf;
3401 es.dwCookie = (DWORD_PTR)&p;
3402 es.dwError = 0;
3403 es.pfnCallback = test_WM_SETTEXT_esCallback;
3404 memset(buf, 0, sizeof(buf));
3405 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3406 r = strlen(buf);
3407 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3408 ok(strcmp(buf, TestItem1) == 0,
3409 "streamed text different, got %s\n", buf);
3411 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3412 p = buf;
3413 es.dwCookie = (DWORD_PTR)&p;
3414 es.dwError = 0;
3415 es.pfnCallback = test_WM_SETTEXT_esCallback;
3416 memset(buf, 0, sizeof(buf));
3417 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3418 r = strlen(buf);
3419 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3420 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3421 ok(strcmp(buf, TestItem3) == 0,
3422 "streamed text different from, got %s\n", buf);
3423 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3424 p = buf;
3425 es.dwCookie = (DWORD_PTR)&p;
3426 es.dwError = 0;
3427 es.pfnCallback = test_WM_SETTEXT_esCallback;
3428 memset(buf, 0, sizeof(buf));
3429 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3430 r = strlen(buf);
3431 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3432 ok(strcmp(buf, TestItem3) == 0,
3433 "streamed text different, got %s\n", buf);
3435 DestroyWindow(hwndRichEdit);
3438 static void test_EM_STREAMOUT_FONTTBL(void)
3440 HWND hwndRichEdit = new_richedit(NULL);
3441 EDITSTREAM es;
3442 char buf[1024] = {0};
3443 char * p;
3444 char * fontTbl;
3445 int brackCount;
3447 const char * TestItem = "TestSomeText";
3449 /* fills in the richedit control with some text */
3450 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3452 /* streams out the text in rtf format */
3453 p = buf;
3454 es.dwCookie = (DWORD_PTR)&p;
3455 es.dwError = 0;
3456 es.pfnCallback = test_WM_SETTEXT_esCallback;
3457 memset(buf, 0, sizeof(buf));
3458 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3460 /* scans for \fonttbl, error if not found */
3461 fontTbl = strstr(buf, "\\fonttbl");
3462 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3463 if(fontTbl)
3465 /* scans for terminating closing bracket */
3466 brackCount = 1;
3467 while(*fontTbl && brackCount)
3469 if(*fontTbl == '{')
3470 brackCount++;
3471 else if(*fontTbl == '}')
3472 brackCount--;
3473 fontTbl++;
3475 /* checks whether closing bracket is ok */
3476 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3477 if(!brackCount)
3479 /* char before closing fonttbl block should be a closed bracket */
3480 fontTbl -= 2;
3481 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3483 /* char after fonttbl block should be a crlf */
3484 fontTbl += 2;
3485 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3488 DestroyWindow(hwndRichEdit);
3492 static void test_EM_SETTEXTEX(void)
3494 HWND hwndRichEdit, parent;
3495 SCROLLINFO si;
3496 int sel_start, sel_end;
3497 SETTEXTEX setText;
3498 GETTEXTEX getText;
3499 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3500 'S', 'o', 'm', 'e',
3501 'T', 'e', 'x', 't', 0};
3502 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3503 't', 'S', 'o', 'm',
3504 'e', 'T', 'e', 'x',
3505 't', 't', 'S', 'o',
3506 'm', 'e', 'T', 'e',
3507 'x', 't', 0};
3508 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3509 '\r','t','S','o','m','e','T','e','x','t',0};
3510 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3511 'S', 'o', 'm', 'e',
3512 'T', 'e', 'x', 't',
3513 '\r', 0};
3514 const char * TestItem2_after = "TestSomeText\r\n";
3515 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3516 'S', 'o', 'm', 'e',
3517 'T', 'e', 'x', 't',
3518 '\r','\n','\r','\n', 0};
3519 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3520 'S', 'o', 'm', 'e',
3521 'T', 'e', 'x', 't',
3522 '\n','\n', 0};
3523 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3524 'S', 'o', 'm', 'e',
3525 'T', 'e', 'x', 't',
3526 '\r','\r', 0};
3527 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3528 'S', 'o', 'm', 'e',
3529 'T', 'e', 'x', 't',
3530 '\r','\r','\n','\r',
3531 '\n', 0};
3532 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3533 'S', 'o', 'm', 'e',
3534 'T', 'e', 'x', 't',
3535 ' ','\r', 0};
3536 #define MAX_BUF_LEN 1024
3537 WCHAR buf[MAX_BUF_LEN];
3538 char bufACP[MAX_BUF_LEN];
3539 char * p;
3540 int result;
3541 CHARRANGE cr;
3542 EDITSTREAM es;
3543 WNDCLASSA cls;
3545 /* Test the scroll position with and without a parent window.
3547 * For some reason the scroll position is 0 after EM_SETTEXTEX
3548 * with the ST_SELECTION flag only when the control has a parent
3549 * window, even though the selection is at the end. */
3550 cls.style = 0;
3551 cls.lpfnWndProc = DefWindowProcA;
3552 cls.cbClsExtra = 0;
3553 cls.cbWndExtra = 0;
3554 cls.hInstance = GetModuleHandleA(0);
3555 cls.hIcon = 0;
3556 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3557 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3558 cls.lpszMenuName = NULL;
3559 cls.lpszClassName = "ParentTestClass";
3560 if(!RegisterClassA(&cls)) assert(0);
3562 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3563 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3564 ok (parent != 0, "Failed to create parent window\n");
3566 hwndRichEdit = CreateWindowEx(0,
3567 RICHEDIT_CLASS, NULL,
3568 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3569 0, 0, 200, 60, parent, NULL,
3570 hmoduleRichEdit, NULL);
3572 setText.codepage = CP_ACP;
3573 setText.flags = ST_SELECTION;
3574 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3575 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3576 si.cbSize = sizeof(si);
3577 si.fMask = SIF_ALL;
3578 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3579 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3580 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3581 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3582 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3584 DestroyWindow(parent);
3586 /* Test without a parent window */
3587 hwndRichEdit = new_richedit(NULL);
3588 setText.codepage = CP_ACP;
3589 setText.flags = ST_SELECTION;
3590 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3591 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3592 si.cbSize = sizeof(si);
3593 si.fMask = SIF_ALL;
3594 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3595 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3596 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3597 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3598 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3600 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3601 * but this time it is because the selection is at the beginning. */
3602 setText.codepage = CP_ACP;
3603 setText.flags = ST_DEFAULT;
3604 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3605 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3606 si.cbSize = sizeof(si);
3607 si.fMask = SIF_ALL;
3608 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3609 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3610 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3611 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3612 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3614 setText.codepage = 1200; /* no constant for unicode */
3615 getText.codepage = 1200; /* no constant for unicode */
3616 getText.cb = MAX_BUF_LEN;
3617 getText.flags = GT_DEFAULT;
3618 getText.lpDefaultChar = NULL;
3619 getText.lpUsedDefChar = NULL;
3621 setText.flags = 0;
3622 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3623 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3624 ok(lstrcmpW(buf, TestItem1) == 0,
3625 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3627 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3628 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3630 setText.codepage = 1200; /* no constant for unicode */
3631 getText.codepage = 1200; /* no constant for unicode */
3632 getText.cb = MAX_BUF_LEN;
3633 getText.flags = GT_DEFAULT;
3634 getText.lpDefaultChar = NULL;
3635 getText.lpUsedDefChar = NULL;
3636 setText.flags = 0;
3637 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3638 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3639 ok(lstrcmpW(buf, TestItem2) == 0,
3640 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3642 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3643 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3644 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3645 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3647 /* Baseline test for just-enough buffer space for string */
3648 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3649 getText.codepage = 1200; /* no constant for unicode */
3650 getText.flags = GT_DEFAULT;
3651 getText.lpDefaultChar = NULL;
3652 getText.lpUsedDefChar = NULL;
3653 memset(buf, 0, MAX_BUF_LEN);
3654 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3655 ok(lstrcmpW(buf, TestItem2) == 0,
3656 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3658 /* When there is enough space for one character, but not both, of the CRLF
3659 pair at the end of the string, the CR is not copied at all. That is,
3660 the caller must not see CRLF pairs truncated to CR at the end of the
3661 string.
3663 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3664 getText.codepage = 1200; /* no constant for unicode */
3665 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3666 getText.lpDefaultChar = NULL;
3667 getText.lpUsedDefChar = NULL;
3668 memset(buf, 0, MAX_BUF_LEN);
3669 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3670 ok(lstrcmpW(buf, TestItem1) == 0,
3671 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3674 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3675 setText.codepage = 1200; /* no constant for unicode */
3676 getText.codepage = 1200; /* no constant for unicode */
3677 getText.cb = MAX_BUF_LEN;
3678 getText.flags = GT_DEFAULT;
3679 getText.lpDefaultChar = NULL;
3680 getText.lpUsedDefChar = NULL;
3681 setText.flags = 0;
3682 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3683 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3684 ok(lstrcmpW(buf, TestItem3_after) == 0,
3685 "EM_SETTEXTEX did not convert properly\n");
3687 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3688 setText.codepage = 1200; /* no constant for unicode */
3689 getText.codepage = 1200; /* no constant for unicode */
3690 getText.cb = MAX_BUF_LEN;
3691 getText.flags = GT_DEFAULT;
3692 getText.lpDefaultChar = NULL;
3693 getText.lpUsedDefChar = NULL;
3694 setText.flags = 0;
3695 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3696 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3697 ok(lstrcmpW(buf, TestItem3_after) == 0,
3698 "EM_SETTEXTEX did not convert properly\n");
3700 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3701 setText.codepage = 1200; /* no constant for unicode */
3702 getText.codepage = 1200; /* no constant for unicode */
3703 getText.cb = MAX_BUF_LEN;
3704 getText.flags = GT_DEFAULT;
3705 getText.lpDefaultChar = NULL;
3706 getText.lpUsedDefChar = NULL;
3707 setText.flags = 0;
3708 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3709 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3710 ok(lstrcmpW(buf, TestItem4_after) == 0,
3711 "EM_SETTEXTEX did not convert properly\n");
3713 /* !ST_SELECTION && Unicode && !\rtf */
3714 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3715 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3717 ok (result == 1,
3718 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3719 ok(lstrlenW(buf) == 0,
3720 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3722 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3723 setText.flags = 0;
3724 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3725 /* select some text */
3726 cr.cpMax = 1;
3727 cr.cpMin = 3;
3728 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3729 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3730 setText.flags = ST_SELECTION;
3731 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3732 ok(result == 0,
3733 "EM_SETTEXTEX with NULL lParam to replace selection"
3734 " with no text should return 0. Got %i\n",
3735 result);
3737 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3738 setText.flags = 0;
3739 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3740 /* select some text */
3741 cr.cpMax = 1;
3742 cr.cpMin = 3;
3743 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3744 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3745 setText.flags = ST_SELECTION;
3746 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3747 (WPARAM)&setText, (LPARAM) TestItem1);
3748 /* get text */
3749 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3750 ok(result == lstrlenW(TestItem1),
3751 "EM_SETTEXTEX with NULL lParam to replace selection"
3752 " with no text should return 0. Got %i\n",
3753 result);
3754 ok(lstrlenW(buf) == 22,
3755 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3756 lstrlenW(buf) );
3758 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3759 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3760 p = (char *)buf;
3761 es.dwCookie = (DWORD_PTR)&p;
3762 es.dwError = 0;
3763 es.pfnCallback = test_WM_SETTEXT_esCallback;
3764 memset(buf, 0, sizeof(buf));
3765 SendMessage(hwndRichEdit, EM_STREAMOUT,
3766 (WPARAM)(SF_RTF), (LPARAM)&es);
3767 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3769 /* !ST_SELECTION && !Unicode && \rtf */
3770 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3771 getText.codepage = 1200; /* no constant for unicode */
3772 getText.cb = MAX_BUF_LEN;
3773 getText.flags = GT_DEFAULT;
3774 getText.lpDefaultChar = NULL;
3775 getText.lpUsedDefChar = NULL;
3777 setText.flags = 0;
3778 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3779 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3780 ok(lstrcmpW(buf, TestItem1) == 0,
3781 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3783 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3784 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3785 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3786 getText.codepage = CP_ACP;
3787 getText.cb = MAX_BUF_LEN;
3788 getText.flags = GT_DEFAULT;
3789 getText.lpDefaultChar = NULL;
3790 getText.lpUsedDefChar = NULL;
3792 setText.flags = ST_SELECTION;
3793 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3794 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3795 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3796 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3797 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3799 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3800 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3801 p = (char *)buf;
3802 es.dwCookie = (DWORD_PTR)&p;
3803 es.dwError = 0;
3804 es.pfnCallback = test_WM_SETTEXT_esCallback;
3805 memset(buf, 0, sizeof(buf));
3806 SendMessage(hwndRichEdit, EM_STREAMOUT,
3807 (WPARAM)(SF_RTF), (LPARAM)&es);
3808 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3810 /* select some text */
3811 cr.cpMax = 1;
3812 cr.cpMin = 3;
3813 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3815 /* ST_SELECTION && !Unicode && \rtf */
3816 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3817 getText.codepage = 1200; /* no constant for unicode */
3818 getText.cb = MAX_BUF_LEN;
3819 getText.flags = GT_DEFAULT;
3820 getText.lpDefaultChar = NULL;
3821 getText.lpUsedDefChar = NULL;
3823 setText.flags = ST_SELECTION;
3824 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3825 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3826 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3828 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3829 setText.codepage = 1200; /* no constant for unicode */
3830 getText.codepage = CP_ACP;
3831 getText.cb = MAX_BUF_LEN;
3833 setText.flags = 0;
3834 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3835 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3837 /* select some text */
3838 cr.cpMax = 1;
3839 cr.cpMin = 3;
3840 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3842 /* ST_SELECTION && !Unicode && !\rtf */
3843 setText.codepage = CP_ACP;
3844 getText.codepage = 1200; /* no constant for unicode */
3845 getText.cb = MAX_BUF_LEN;
3846 getText.flags = GT_DEFAULT;
3847 getText.lpDefaultChar = NULL;
3848 getText.lpUsedDefChar = NULL;
3850 setText.flags = ST_SELECTION;
3851 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3852 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3853 ok(lstrcmpW(buf, TestItem1alt) == 0,
3854 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3855 " using ST_SELECTION and non-Unicode\n");
3857 /* Test setting text using rich text format */
3858 setText.flags = 0;
3859 setText.codepage = CP_ACP;
3860 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3861 getText.codepage = CP_ACP;
3862 getText.cb = MAX_BUF_LEN;
3863 getText.flags = GT_DEFAULT;
3864 getText.lpDefaultChar = NULL;
3865 getText.lpUsedDefChar = NULL;
3866 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3867 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3869 setText.flags = 0;
3870 setText.codepage = CP_ACP;
3871 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3872 getText.codepage = CP_ACP;
3873 getText.cb = MAX_BUF_LEN;
3874 getText.flags = GT_DEFAULT;
3875 getText.lpDefaultChar = NULL;
3876 getText.lpUsedDefChar = NULL;
3877 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3878 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3880 DestroyWindow(hwndRichEdit);
3883 static void test_EM_LIMITTEXT(void)
3885 int ret;
3887 HWND hwndRichEdit = new_richedit(NULL);
3889 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3890 * about setting the length to -1 for multiline edit controls doesn't happen.
3893 /* Don't check default gettextlimit case. That's done in other tests */
3895 /* Set textlimit to 100 */
3896 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3897 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3898 ok (ret == 100,
3899 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3901 /* Set textlimit to 0 */
3902 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3903 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3904 ok (ret == 65536,
3905 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3907 /* Set textlimit to -1 */
3908 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3909 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3910 ok (ret == -1,
3911 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3913 /* Set textlimit to -2 */
3914 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3915 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3916 ok (ret == -2,
3917 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3919 DestroyWindow (hwndRichEdit);
3923 static void test_EM_EXLIMITTEXT(void)
3925 int i, selBegin, selEnd, len1, len2;
3926 int result;
3927 char text[1024 + 1];
3928 char buffer[1024 + 1];
3929 int textlimit = 0; /* multiple of 100 */
3930 HWND hwndRichEdit = new_richedit(NULL);
3932 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3933 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3935 textlimit = 256000;
3936 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3937 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3938 /* set higher */
3939 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3941 textlimit = 1000;
3942 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3943 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3944 /* set lower */
3945 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3947 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3948 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3949 /* default for WParam = 0 */
3950 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3952 textlimit = sizeof(text)-1;
3953 memset(text, 'W', textlimit);
3954 text[sizeof(text)-1] = 0;
3955 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3956 /* maxed out text */
3957 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3959 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3960 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3961 len1 = selEnd - selBegin;
3963 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3964 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3965 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3966 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3967 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3968 len2 = selEnd - selBegin;
3970 ok(len1 != len2,
3971 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3972 len1,len2,i);
3974 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3975 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3976 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3977 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3978 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3979 len1 = selEnd - selBegin;
3981 ok(len1 != len2,
3982 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3983 len1,len2,i);
3985 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3986 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3987 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3988 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3989 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3990 len2 = selEnd - selBegin;
3992 ok(len1 == len2,
3993 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3994 len1,len2,i);
3996 /* set text up to the limit, select all the text, then add a char */
3997 textlimit = 5;
3998 memset(text, 'W', textlimit);
3999 text[textlimit] = 0;
4000 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4001 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4002 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4003 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
4004 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4005 result = strcmp(buffer, "A");
4006 ok(0 == result, "got string = \"%s\"\n", buffer);
4008 /* WM_SETTEXT not limited */
4009 textlimit = 10;
4010 memset(text, 'W', textlimit);
4011 text[textlimit] = 0;
4012 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4013 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4014 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4015 i = strlen(buffer);
4016 ok(10 == i, "expected 10 chars\n");
4017 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4018 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4020 /* try inserting more text at end */
4021 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4022 ok(0 == i, "WM_CHAR wasn't processed\n");
4023 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4024 i = strlen(buffer);
4025 ok(10 == i, "expected 10 chars, got %i\n", i);
4026 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4027 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4029 /* try inserting text at beginning */
4030 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4031 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4032 ok(0 == i, "WM_CHAR wasn't processed\n");
4033 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4034 i = strlen(buffer);
4035 ok(10 == i, "expected 10 chars, got %i\n", i);
4036 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4037 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4039 /* WM_CHAR is limited */
4040 textlimit = 1;
4041 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4042 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4043 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4044 ok(0 == i, "WM_CHAR wasn't processed\n");
4045 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4046 ok(0 == i, "WM_CHAR wasn't processed\n");
4047 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4048 i = strlen(buffer);
4049 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4051 DestroyWindow(hwndRichEdit);
4054 static void test_EM_GETLIMITTEXT(void)
4056 int i;
4057 HWND hwndRichEdit = new_richedit(NULL);
4059 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4060 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4062 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4063 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4064 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4066 DestroyWindow(hwndRichEdit);
4069 static void test_WM_SETFONT(void)
4071 /* There is no invalid input or error conditions for this function.
4072 * NULL wParam and lParam just fall back to their default values
4073 * It should be noted that even if you use a gibberish name for your fonts
4074 * here, it will still work because the name is stored. They will display as
4075 * System, but will report their name to be whatever they were created as */
4077 HWND hwndRichEdit = new_richedit(NULL);
4078 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4079 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4080 FF_DONTCARE, "Marlett");
4081 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4082 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4083 FF_DONTCARE, "MS Sans Serif");
4084 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4085 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4086 FF_DONTCARE, "Courier");
4087 LOGFONTA sentLogFont;
4088 CHARFORMAT2A returnedCF2A;
4090 returnedCF2A.cbSize = sizeof(returnedCF2A);
4092 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4093 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4094 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4096 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4097 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4098 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4099 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4101 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4102 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4103 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4104 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4105 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4106 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4108 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4109 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4110 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4111 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4112 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4113 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4115 /* This last test is special since we send in NULL. We clear the variables
4116 * and just compare to "System" instead of the sent in font name. */
4117 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4118 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4119 returnedCF2A.cbSize = sizeof(returnedCF2A);
4121 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4122 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4123 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4124 ok (!strcmp("System",returnedCF2A.szFaceName),
4125 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4127 DestroyWindow(hwndRichEdit);
4131 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4132 LPBYTE pbBuff,
4133 LONG cb,
4134 LONG *pcb)
4136 const char** str = (const char**)dwCookie;
4137 int size = strlen(*str);
4138 if(size > 3) /* let's make it piecemeal for fun */
4139 size = 3;
4140 *pcb = cb;
4141 if (*pcb > size) {
4142 *pcb = size;
4144 if (*pcb > 0) {
4145 memcpy(pbBuff, *str, *pcb);
4146 *str += *pcb;
4148 return 0;
4151 static void test_EM_GETMODIFY(void)
4153 HWND hwndRichEdit = new_richedit(NULL);
4154 LRESULT result;
4155 SETTEXTEX setText;
4156 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4157 'S', 'o', 'm', 'e',
4158 'T', 'e', 'x', 't', 0};
4159 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4160 'S', 'o', 'm', 'e',
4161 'O', 't', 'h', 'e', 'r',
4162 'T', 'e', 'x', 't', 0};
4163 const char* streamText = "hello world";
4164 CHARFORMAT2 cf2;
4165 PARAFORMAT2 pf2;
4166 EDITSTREAM es;
4168 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4169 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4170 FF_DONTCARE, "Courier");
4172 setText.codepage = 1200; /* no constant for unicode */
4173 setText.flags = ST_KEEPUNDO;
4176 /* modify flag shouldn't be set when richedit is first created */
4177 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4178 ok (result == 0,
4179 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4181 /* setting modify flag should actually set it */
4182 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4183 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4184 ok (result != 0,
4185 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4187 /* clearing modify flag should actually clear it */
4188 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4189 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4190 ok (result == 0,
4191 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4193 /* setting font doesn't change modify flag */
4194 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4195 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4196 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4197 ok (result == 0,
4198 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4200 /* setting text should set modify flag */
4201 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4202 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4203 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4204 ok (result != 0,
4205 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4207 /* undo previous text doesn't reset modify flag */
4208 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4209 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4210 ok (result != 0,
4211 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4213 /* set text with no flag to keep undo stack should not set modify flag */
4214 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4215 setText.flags = 0;
4216 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4217 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4218 ok (result == 0,
4219 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4221 /* WM_SETTEXT doesn't modify */
4222 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4223 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4224 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4225 ok (result == 0,
4226 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4228 /* clear the text */
4229 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4230 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4231 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4232 ok (result == 0,
4233 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4235 /* replace text */
4236 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4237 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4238 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4239 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4240 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4241 ok (result != 0,
4242 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4244 /* copy/paste text 1 */
4245 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4246 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4247 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4248 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4249 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4250 ok (result != 0,
4251 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4253 /* copy/paste text 2 */
4254 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4255 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4256 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4257 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4258 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4259 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4260 ok (result != 0,
4261 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4263 /* press char */
4264 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4265 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4266 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4267 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4268 ok (result != 0,
4269 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4271 /* press del */
4272 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4273 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4274 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4275 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4276 ok (result != 0,
4277 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4279 /* set char format */
4280 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4281 cf2.cbSize = sizeof(CHARFORMAT2);
4282 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4283 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4284 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4285 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4286 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4287 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4288 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4289 ok (result != 0,
4290 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4292 /* set para format */
4293 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4294 pf2.cbSize = sizeof(PARAFORMAT2);
4295 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4296 (LPARAM) &pf2);
4297 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4298 pf2.wAlignment = PFA_RIGHT;
4299 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4300 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4301 ok (result == 0,
4302 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4304 /* EM_STREAM */
4305 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4306 es.dwCookie = (DWORD_PTR)&streamText;
4307 es.dwError = 0;
4308 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4309 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4310 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4311 ok (result != 0,
4312 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4314 DestroyWindow(hwndRichEdit);
4317 struct exsetsel_s {
4318 LONG min;
4319 LONG max;
4320 LRESULT expected_retval;
4321 int expected_getsel_start;
4322 int expected_getsel_end;
4323 int _getsel_todo_wine;
4326 const struct exsetsel_s exsetsel_tests[] = {
4327 /* sanity tests */
4328 {5, 10, 10, 5, 10, 0},
4329 {15, 17, 17, 15, 17, 0},
4330 /* test cpMax > strlen() */
4331 {0, 100, 18, 0, 18, 1},
4332 /* test cpMin == cpMax */
4333 {5, 5, 5, 5, 5, 0},
4334 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4335 {-1, 0, 5, 5, 5, 0},
4336 {-1, 17, 5, 5, 5, 0},
4337 {-1, 18, 5, 5, 5, 0},
4338 /* test cpMin < 0 && cpMax < 0 */
4339 {-1, -1, 17, 17, 17, 0},
4340 {-4, -5, 17, 17, 17, 0},
4341 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4342 {0, -1, 18, 0, 18, 1},
4343 {17, -5, 18, 17, 18, 1},
4344 {18, -3, 17, 17, 17, 0},
4345 /* test if cpMin > cpMax */
4346 {15, 19, 18, 15, 18, 1},
4347 {19, 15, 18, 15, 18, 1}
4350 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4351 CHARRANGE cr;
4352 LRESULT result;
4353 int start, end;
4355 cr.cpMin = setsel->min;
4356 cr.cpMax = setsel->max;
4357 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4359 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4361 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4363 if (setsel->_getsel_todo_wine) {
4364 todo_wine {
4365 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4367 } else {
4368 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4372 static void test_EM_EXSETSEL(void)
4374 HWND hwndRichEdit = new_richedit(NULL);
4375 int i;
4376 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4378 /* sending some text to the window */
4379 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4380 /* 01234567890123456*/
4381 /* 10 */
4383 for (i = 0; i < num_tests; i++) {
4384 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4387 DestroyWindow(hwndRichEdit);
4390 static void test_EM_REPLACESEL(int redraw)
4392 HWND hwndRichEdit = new_richedit(NULL);
4393 char buffer[1024] = {0};
4394 int r;
4395 GETTEXTEX getText;
4396 CHARRANGE cr;
4398 /* sending some text to the window */
4399 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4400 /* 01234567890123456*/
4401 /* 10 */
4403 /* FIXME add more tests */
4404 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4405 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4406 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4407 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4408 r = strcmp(buffer, "testing");
4409 ok(0 == r, "expected %d, got %d\n", 0, r);
4411 DestroyWindow(hwndRichEdit);
4413 hwndRichEdit = new_richedit(NULL);
4415 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4416 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4418 /* Test behavior with carriage returns and newlines */
4419 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4420 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4421 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4422 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4423 r = strcmp(buffer, "RichEdit1");
4424 ok(0 == r, "expected %d, got %d\n", 0, r);
4425 getText.cb = 1024;
4426 getText.codepage = CP_ACP;
4427 getText.flags = GT_DEFAULT;
4428 getText.lpDefaultChar = NULL;
4429 getText.lpUsedDefChar = NULL;
4430 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4431 ok(strcmp(buffer, "RichEdit1") == 0,
4432 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4434 /* Test number of lines reported after EM_REPLACESEL */
4435 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4436 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4438 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4439 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4440 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4441 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4442 r = strcmp(buffer, "RichEdit1\r\n");
4443 ok(0 == r, "expected %d, got %d\n", 0, r);
4444 getText.cb = 1024;
4445 getText.codepage = CP_ACP;
4446 getText.flags = GT_DEFAULT;
4447 getText.lpDefaultChar = NULL;
4448 getText.lpUsedDefChar = NULL;
4449 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4450 ok(strcmp(buffer, "RichEdit1\r") == 0,
4451 "EM_GETTEXTEX returned incorrect string\n");
4453 /* Test number of lines reported after EM_REPLACESEL */
4454 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4455 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4457 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4458 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4459 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4461 /* Test number of lines reported after EM_REPLACESEL */
4462 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4463 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4465 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4466 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4467 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4468 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4470 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4471 r = strcmp(buffer, "RichEdit1\r\n");
4472 ok(0 == r, "expected %d, got %d\n", 0, r);
4473 getText.cb = 1024;
4474 getText.codepage = CP_ACP;
4475 getText.flags = GT_DEFAULT;
4476 getText.lpDefaultChar = NULL;
4477 getText.lpUsedDefChar = NULL;
4478 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4479 ok(strcmp(buffer, "RichEdit1\r") == 0,
4480 "EM_GETTEXTEX returned incorrect string\n");
4482 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4483 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4484 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4485 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4487 /* The following tests show that richedit should handle the special \r\r\n
4488 sequence by turning it into a single space on insertion. However,
4489 EM_REPLACESEL on WinXP returns the number of characters in the original
4490 string.
4493 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4494 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4495 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4496 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4497 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4498 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4499 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4501 /* Test the actual string */
4502 getText.cb = 1024;
4503 getText.codepage = CP_ACP;
4504 getText.flags = GT_DEFAULT;
4505 getText.lpDefaultChar = NULL;
4506 getText.lpUsedDefChar = NULL;
4507 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4508 ok(strcmp(buffer, "\r\r") == 0,
4509 "EM_GETTEXTEX returned incorrect string\n");
4511 /* Test number of lines reported after EM_REPLACESEL */
4512 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4513 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4515 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4516 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4517 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4518 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4519 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4520 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4521 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4523 /* Test the actual string */
4524 getText.cb = 1024;
4525 getText.codepage = CP_ACP;
4526 getText.flags = GT_DEFAULT;
4527 getText.lpDefaultChar = NULL;
4528 getText.lpUsedDefChar = NULL;
4529 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4530 ok(strcmp(buffer, " ") == 0,
4531 "EM_GETTEXTEX returned incorrect string\n");
4533 /* Test number of lines reported after EM_REPLACESEL */
4534 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4535 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4537 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4538 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4539 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4540 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4541 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4542 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4543 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4545 /* Test the actual string */
4546 getText.cb = 1024;
4547 getText.codepage = CP_ACP;
4548 getText.flags = GT_DEFAULT;
4549 getText.lpDefaultChar = NULL;
4550 getText.lpUsedDefChar = NULL;
4551 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4552 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4553 "EM_GETTEXTEX returned incorrect string\n");
4555 /* Test number of lines reported after EM_REPLACESEL */
4556 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4557 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4559 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4560 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4561 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4562 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4563 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4564 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4565 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4567 /* Test the actual string */
4568 getText.cb = 1024;
4569 getText.codepage = CP_ACP;
4570 getText.flags = GT_DEFAULT;
4571 getText.lpDefaultChar = NULL;
4572 getText.lpUsedDefChar = NULL;
4573 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4574 ok(strcmp(buffer, " \r") == 0,
4575 "EM_GETTEXTEX returned incorrect string\n");
4577 /* Test number of lines reported after EM_REPLACESEL */
4578 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4579 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4581 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4582 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4583 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4584 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4585 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4586 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4587 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4589 /* Test the actual string */
4590 getText.cb = 1024;
4591 getText.codepage = CP_ACP;
4592 getText.flags = GT_DEFAULT;
4593 getText.lpDefaultChar = NULL;
4594 getText.lpUsedDefChar = NULL;
4595 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4596 ok(strcmp(buffer, " \r\r") == 0,
4597 "EM_GETTEXTEX returned incorrect string\n");
4599 /* Test number of lines reported after EM_REPLACESEL */
4600 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4601 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4603 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4604 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4605 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4606 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4607 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4608 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4609 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4611 /* Test the actual string */
4612 getText.cb = 1024;
4613 getText.codepage = CP_ACP;
4614 getText.flags = GT_DEFAULT;
4615 getText.lpDefaultChar = NULL;
4616 getText.lpUsedDefChar = NULL;
4617 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4618 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4619 "EM_GETTEXTEX returned incorrect string\n");
4621 /* Test number of lines reported after EM_REPLACESEL */
4622 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4623 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4625 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4626 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4627 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4628 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4629 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4630 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4631 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4633 /* Test the actual string */
4634 getText.cb = 1024;
4635 getText.codepage = CP_ACP;
4636 getText.flags = GT_DEFAULT;
4637 getText.lpDefaultChar = NULL;
4638 getText.lpUsedDefChar = NULL;
4639 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4640 ok(strcmp(buffer, "\r\r") == 0,
4641 "EM_GETTEXTEX returned incorrect string\n");
4643 /* Test number of lines reported after EM_REPLACESEL */
4644 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4645 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4647 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4648 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4649 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4650 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4651 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4652 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4653 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4655 /* Test the actual string */
4656 getText.cb = 1024;
4657 getText.codepage = CP_ACP;
4658 getText.flags = GT_DEFAULT;
4659 getText.lpDefaultChar = NULL;
4660 getText.lpUsedDefChar = NULL;
4661 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4662 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4663 "EM_GETTEXTEX returned incorrect string\n");
4665 /* Test number of lines reported after EM_REPLACESEL */
4666 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4667 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4669 if (!redraw)
4670 /* This is needed to avoid interferring with keybd_event calls
4671 * on other tests that simulate keyboard events. */
4672 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4674 DestroyWindow(hwndRichEdit);
4677 static void test_WM_PASTE(void)
4679 int result;
4680 char buffer[1024] = {0};
4681 const char* text1 = "testing paste\r";
4682 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4683 const char* text1_after = "testing paste\r\n";
4684 const char* text2 = "testing paste\r\rtesting paste";
4685 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4686 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4687 HWND hwndRichEdit = new_richedit(NULL);
4689 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4690 * to test the state of the modifiers (Ctrl/Alt/Shift).
4692 * Therefore Ctrl-<key> keystrokes need to be simulated with
4693 * keybd_event or by using SetKeyboardState to set the modifiers
4694 * and SendMessage to simulate the keystrokes.
4697 /* Sent keystrokes with keybd_event */
4698 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4699 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4700 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4701 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4702 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4704 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4705 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4707 SEND_CTRL_C(hwndRichEdit); /* Copy */
4708 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4709 SEND_CTRL_V(hwndRichEdit); /* Paste */
4710 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4711 /* Pasted text should be visible at this step */
4712 result = strcmp(text1_step1, buffer);
4713 ok(result == 0,
4714 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4716 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4717 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4718 /* Text should be the same as before (except for \r -> \r\n conversion) */
4719 result = strcmp(text1_after, buffer);
4720 ok(result == 0,
4721 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4723 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4724 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4725 SEND_CTRL_C(hwndRichEdit); /* Copy */
4726 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4727 SEND_CTRL_V(hwndRichEdit); /* Paste */
4728 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4729 /* Pasted text should be visible at this step */
4730 result = strcmp(text3, buffer);
4731 ok(result == 0,
4732 "test paste: strcmp = %i\n", result);
4733 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4734 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4735 /* Text should be the same as before (except for \r -> \r\n conversion) */
4736 result = strcmp(text2_after, buffer);
4737 ok(result == 0,
4738 "test paste: strcmp = %i\n", result);
4739 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4740 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4741 /* Text should revert to post-paste state */
4742 result = strcmp(buffer,text3);
4743 ok(result == 0,
4744 "test paste: strcmp = %i\n", result);
4746 #undef SEND_CTRL_C
4747 #undef SEND_CTRL_X
4748 #undef SEND_CTRL_V
4749 #undef SEND_CTRL_Z
4750 #undef SEND_CTRL_Y
4752 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4753 /* Send WM_CHAR to simulates Ctrl-V */
4754 SendMessage(hwndRichEdit, WM_CHAR, 22,
4755 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4756 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4757 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4758 result = strcmp(buffer,"");
4759 ok(result == 0,
4760 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4762 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4763 * with SetKeyboard state. */
4765 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4766 /* Simulates paste (Ctrl-V) */
4767 hold_key(VK_CONTROL);
4768 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4769 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4770 release_key(VK_CONTROL);
4771 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4772 result = strcmp(buffer,"paste");
4773 ok(result == 0,
4774 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4776 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4777 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4778 /* Simulates copy (Ctrl-C) */
4779 hold_key(VK_CONTROL);
4780 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4781 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) | 1);
4782 release_key(VK_CONTROL);
4783 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4784 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4785 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4786 result = strcmp(buffer,"testing");
4787 ok(result == 0,
4788 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4790 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4791 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4792 /* Simulates select all (Ctrl-A) */
4793 hold_key(VK_CONTROL);
4794 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4795 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) | 1);
4796 /* Simulates select cut (Ctrl-X) */
4797 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4798 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) | 1);
4799 release_key(VK_CONTROL);
4800 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4801 result = strcmp(buffer,"");
4802 ok(result == 0,
4803 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4804 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4805 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4806 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4807 result = strcmp(buffer,"cut\r\n");
4808 todo_wine ok(result == 0,
4809 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4810 /* Simulates undo (Ctrl-Z) */
4811 hold_key(VK_CONTROL);
4812 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4813 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4814 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4815 result = strcmp(buffer,"");
4816 ok(result == 0,
4817 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4818 /* Simulates redo (Ctrl-Y) */
4819 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4820 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4821 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4822 result = strcmp(buffer,"cut\r\n");
4823 todo_wine ok(result == 0,
4824 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4825 release_key(VK_CONTROL);
4827 DestroyWindow(hwndRichEdit);
4830 static void test_EM_FORMATRANGE(void)
4832 int r, i, tpp_x, tpp_y;
4833 HDC hdc;
4834 HWND hwndRichEdit = new_richedit(NULL);
4835 FORMATRANGE fr;
4836 BOOL skip_non_english;
4837 static const struct {
4838 const char *string; /* The string */
4839 int first; /* First 'pagebreak', 0 for don't care */
4840 int second; /* Second 'pagebreak', 0 for don't care */
4841 } fmtstrings[] = {
4842 {"WINE wine", 0, 0},
4843 {"WINE wineWine", 0, 0},
4844 {"WINE\r\nwine\r\nwine", 5, 10},
4845 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4846 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4849 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4850 if (skip_non_english)
4851 skip("Skipping some tests on non-English platform\n");
4853 hdc = GetDC(hwndRichEdit);
4854 ok(hdc != NULL, "Could not get HDC\n");
4856 /* Calculate the twips per pixel */
4857 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4858 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4860 /* Test the simple case where all the text fits in the page rect. */
4861 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4862 fr.hdc = fr.hdcTarget = hdc;
4863 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4864 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4865 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4866 fr.chrg.cpMin = 0;
4867 fr.chrg.cpMax = -1;
4868 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4869 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4871 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4872 fr.rc.bottom = fr.rcPage.bottom;
4873 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4874 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4876 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4878 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4880 GETTEXTLENGTHEX gtl;
4881 SIZE stringsize;
4882 int len;
4884 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4886 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4887 gtl.codepage = CP_ACP;
4888 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4890 /* Get some size information for the string */
4891 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4893 /* Define the box to be half the width needed and a bit larger than the height.
4894 * Changes to the width means we have at least 2 pages. Changes to the height
4895 * is done so we can check the changing of fr.rc.bottom.
4897 fr.hdc = fr.hdcTarget = hdc;
4898 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4899 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4900 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4902 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4903 todo_wine {
4904 ok(r == len, "Expected %d, got %d\n", len, r);
4907 /* We know that the page can't hold the full string. See how many characters
4908 * are on the first one
4910 fr.chrg.cpMin = 0;
4911 fr.chrg.cpMax = -1;
4912 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4913 todo_wine {
4914 if (! skip_non_english)
4915 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4917 if (fmtstrings[i].first)
4918 todo_wine {
4919 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4921 else
4922 ok(r < len, "Expected < %d, got %d\n", len, r);
4924 /* Do another page */
4925 fr.chrg.cpMin = r;
4926 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4927 if (fmtstrings[i].second)
4928 todo_wine {
4929 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4931 else if (! skip_non_english)
4932 ok (r < len, "Expected < %d, got %d\n", len, r);
4934 /* There is at least on more page, but we don't care */
4936 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4937 todo_wine {
4938 ok(r == len, "Expected %d, got %d\n", len, r);
4942 ReleaseDC(NULL, hdc);
4943 DestroyWindow(hwndRichEdit);
4946 static int nCallbackCount = 0;
4948 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4949 LONG cb, LONG* pcb)
4951 const char text[] = {'t','e','s','t'};
4953 if (sizeof(text) <= cb)
4955 if ((int)dwCookie != nCallbackCount)
4957 *pcb = 0;
4958 return 0;
4961 memcpy (pbBuff, text, sizeof(text));
4962 *pcb = sizeof(text);
4964 nCallbackCount++;
4966 return 0;
4968 else
4969 return 1; /* indicates callback failed */
4972 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4973 LPBYTE pbBuff,
4974 LONG cb,
4975 LONG *pcb)
4977 const char** str = (const char**)dwCookie;
4978 int size = strlen(*str);
4979 *pcb = cb;
4980 if (*pcb > size) {
4981 *pcb = size;
4983 if (*pcb > 0) {
4984 memcpy(pbBuff, *str, *pcb);
4985 *str += *pcb;
4987 return 0;
4990 struct StringWithLength {
4991 int length;
4992 char *buffer;
4995 /* This callback is used to handled the null characters in a string. */
4996 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4997 LPBYTE pbBuff,
4998 LONG cb,
4999 LONG *pcb)
5001 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5002 int size = str->length;
5003 *pcb = cb;
5004 if (*pcb > size) {
5005 *pcb = size;
5007 if (*pcb > 0) {
5008 memcpy(pbBuff, str->buffer, *pcb);
5009 str->buffer += *pcb;
5010 str->length -= *pcb;
5012 return 0;
5015 static void test_EM_STREAMIN(void)
5017 HWND hwndRichEdit = new_richedit(NULL);
5018 LRESULT result;
5019 EDITSTREAM es;
5020 char buffer[1024] = {0};
5022 const char * streamText0 = "{\\rtf1 TestSomeText}";
5023 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5024 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5026 const char * streamText1 =
5027 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5028 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5029 "}\r\n";
5031 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5032 const char * streamText2 =
5033 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5034 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5035 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5036 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5037 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5038 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5039 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5041 const char * streamText3 = "RichEdit1";
5043 const char * streamText4 =
5044 "This text just needs to be long enough to cause run to be split onto "
5045 "two separate lines and make sure the null terminating character is "
5046 "handled properly.\0";
5047 int length4 = strlen(streamText4) + 1;
5048 struct StringWithLength cookieForStream4 = {
5049 length4,
5050 (char *)streamText4,
5053 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5054 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5055 struct StringWithLength cookieForStream5 = {
5056 sizeof(streamText5),
5057 (char *)streamText5,
5060 /* Minimal test without \par at the end */
5061 es.dwCookie = (DWORD_PTR)&streamText0;
5062 es.dwError = 0;
5063 es.pfnCallback = test_EM_STREAMIN_esCallback;
5064 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5065 todo_wine ok(result == 12, "got %ld, expected %d\n", result, 12);
5067 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5068 ok (result == 12,
5069 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5070 result = strcmp (buffer,"TestSomeText");
5071 ok (result == 0,
5072 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5073 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5075 /* Native richedit 2.0 ignores last \par */
5076 es.dwCookie = (DWORD_PTR)&streamText0a;
5077 es.dwError = 0;
5078 es.pfnCallback = test_EM_STREAMIN_esCallback;
5079 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5080 todo_wine ok(result == 12, "got %ld, expected %d\n", result, 12);
5082 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5083 ok (result == 12,
5084 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5085 result = strcmp (buffer,"TestSomeText");
5086 ok (result == 0,
5087 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5088 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5090 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5091 es.dwCookie = (DWORD_PTR)&streamText0b;
5092 es.dwError = 0;
5093 es.pfnCallback = test_EM_STREAMIN_esCallback;
5094 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5095 todo_wine ok(result == 13, "got %ld, expected %d\n", result, 13);
5097 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5098 ok (result == 14,
5099 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5100 result = strcmp (buffer,"TestSomeText\r\n");
5101 ok (result == 0,
5102 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5103 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5105 es.dwCookie = (DWORD_PTR)&streamText1;
5106 es.dwError = 0;
5107 es.pfnCallback = test_EM_STREAMIN_esCallback;
5108 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5109 todo_wine ok(result == 12, "got %ld, expected %d\n", result, 12);
5111 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5112 ok (result == 12,
5113 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5114 result = strcmp (buffer,"TestSomeText");
5115 ok (result == 0,
5116 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5117 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5119 es.dwCookie = (DWORD_PTR)&streamText2;
5120 es.dwError = 0;
5121 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5122 ok(result == 0, "got %ld, expected %d\n", result, 0);
5124 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5125 ok (result == 0,
5126 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5127 ok (strlen(buffer) == 0,
5128 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5129 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5131 es.dwCookie = (DWORD_PTR)&streamText3;
5132 es.dwError = 0;
5133 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5134 ok(result == 0, "got %ld, expected %d\n", result, 0);
5136 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5137 ok (result == 0,
5138 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5139 ok (strlen(buffer) == 0,
5140 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5141 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5143 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5144 es.dwError = 0;
5145 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5146 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5147 ok(result == length4, "got %ld, expected %d\n", result, length4);
5149 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5150 ok (result == length4,
5151 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5152 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5154 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5155 es.dwError = 0;
5156 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5157 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5158 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5160 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5161 ok (result == length5,
5162 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length5);
5163 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5165 DestroyWindow(hwndRichEdit);
5168 static void test_EM_StreamIn_Undo(void)
5170 /* The purpose of this test is to determine when a EM_StreamIn should be
5171 * undoable. This is important because WM_PASTE currently uses StreamIn and
5172 * pasting should always be undoable but streaming isn't always.
5174 * cases to test:
5175 * StreamIn plain text without SFF_SELECTION.
5176 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5177 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5178 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5179 * Feel free to add tests for other text modes or StreamIn things.
5183 HWND hwndRichEdit = new_richedit(NULL);
5184 LRESULT result;
5185 EDITSTREAM es;
5186 char buffer[1024] = {0};
5187 const char randomtext[] = "Some text";
5189 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5191 /* StreamIn, no SFF_SELECTION */
5192 es.dwCookie = nCallbackCount;
5193 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5194 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5195 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5196 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5197 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5198 result = strcmp (buffer,"test");
5199 ok (result == 0,
5200 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5202 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5203 ok (result == FALSE,
5204 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5206 /* StreamIn, SFF_SELECTION, but nothing selected */
5207 es.dwCookie = nCallbackCount;
5208 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5209 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5210 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5211 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5212 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5213 result = strcmp (buffer,"testSome text");
5214 ok (result == 0,
5215 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5217 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5218 ok (result == TRUE,
5219 "EM_STREAMIN with SFF_SELECTION but no selection set "
5220 "should create an undo\n");
5222 /* StreamIn, SFF_SELECTION, with a selection */
5223 es.dwCookie = nCallbackCount;
5224 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5225 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5226 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5227 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5228 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5229 result = strcmp (buffer,"Sometesttext");
5230 ok (result == 0,
5231 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5233 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5234 ok (result == TRUE,
5235 "EM_STREAMIN with SFF_SELECTION and selection set "
5236 "should create an undo\n");
5238 DestroyWindow(hwndRichEdit);
5241 static BOOL is_em_settextex_supported(HWND hwnd)
5243 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5244 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5247 static void test_unicode_conversions(void)
5249 static const WCHAR tW[] = {'t',0};
5250 static const WCHAR teW[] = {'t','e',0};
5251 static const WCHAR textW[] = {'t','e','s','t',0};
5252 static const char textA[] = "test";
5253 char bufA[64];
5254 WCHAR bufW[64];
5255 HWND hwnd;
5256 int em_settextex_supported, ret;
5258 #define set_textA(hwnd, wm_set_text, txt) \
5259 do { \
5260 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5261 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5262 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5263 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5264 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5265 } while(0)
5266 #define expect_textA(hwnd, wm_get_text, txt) \
5267 do { \
5268 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5269 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5270 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5271 memset(bufA, 0xAA, sizeof(bufA)); \
5272 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5273 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5274 ret = lstrcmpA(bufA, txt); \
5275 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5276 } while(0)
5278 #define set_textW(hwnd, wm_set_text, txt) \
5279 do { \
5280 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5281 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5282 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5283 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5284 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5285 } while(0)
5286 #define expect_textW(hwnd, wm_get_text, txt) \
5287 do { \
5288 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5289 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5290 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5291 memset(bufW, 0xAA, sizeof(bufW)); \
5292 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5293 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5294 ret = lstrcmpW(bufW, txt); \
5295 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5296 } while(0)
5297 #define expect_empty(hwnd, wm_get_text) \
5298 do { \
5299 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5300 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5301 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5302 memset(bufA, 0xAA, sizeof(bufA)); \
5303 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5304 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5305 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5306 } while(0)
5308 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5309 0, 0, 200, 60, 0, 0, 0, 0);
5310 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5312 ret = IsWindowUnicode(hwnd);
5313 ok(ret, "RichEdit20W should be unicode under NT\n");
5315 /* EM_SETTEXTEX is supported starting from version 3.0 */
5316 em_settextex_supported = is_em_settextex_supported(hwnd);
5317 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5318 em_settextex_supported ? "" : "NOT ");
5320 expect_empty(hwnd, WM_GETTEXT);
5321 expect_empty(hwnd, EM_GETTEXTEX);
5323 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5324 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5325 expect_textA(hwnd, WM_GETTEXT, "t");
5326 expect_textA(hwnd, EM_GETTEXTEX, "t");
5327 expect_textW(hwnd, EM_GETTEXTEX, tW);
5329 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5330 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5331 expect_textA(hwnd, WM_GETTEXT, "te");
5332 expect_textA(hwnd, EM_GETTEXTEX, "te");
5333 expect_textW(hwnd, EM_GETTEXTEX, teW);
5335 set_textA(hwnd, WM_SETTEXT, NULL);
5336 expect_empty(hwnd, WM_GETTEXT);
5337 expect_empty(hwnd, EM_GETTEXTEX);
5339 set_textA(hwnd, WM_SETTEXT, textA);
5340 expect_textA(hwnd, WM_GETTEXT, textA);
5341 expect_textA(hwnd, EM_GETTEXTEX, textA);
5342 expect_textW(hwnd, EM_GETTEXTEX, textW);
5344 if (em_settextex_supported)
5346 set_textA(hwnd, EM_SETTEXTEX, textA);
5347 expect_textA(hwnd, WM_GETTEXT, textA);
5348 expect_textA(hwnd, EM_GETTEXTEX, textA);
5349 expect_textW(hwnd, EM_GETTEXTEX, textW);
5352 set_textW(hwnd, WM_SETTEXT, textW);
5353 expect_textW(hwnd, WM_GETTEXT, textW);
5354 expect_textA(hwnd, WM_GETTEXT, textA);
5355 expect_textW(hwnd, EM_GETTEXTEX, textW);
5356 expect_textA(hwnd, EM_GETTEXTEX, textA);
5358 if (em_settextex_supported)
5360 set_textW(hwnd, EM_SETTEXTEX, textW);
5361 expect_textW(hwnd, WM_GETTEXT, textW);
5362 expect_textA(hwnd, WM_GETTEXT, textA);
5363 expect_textW(hwnd, EM_GETTEXTEX, textW);
5364 expect_textA(hwnd, EM_GETTEXTEX, textA);
5366 DestroyWindow(hwnd);
5368 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5369 0, 0, 200, 60, 0, 0, 0, 0);
5370 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5372 ret = IsWindowUnicode(hwnd);
5373 ok(!ret, "RichEdit20A should NOT be unicode\n");
5375 set_textA(hwnd, WM_SETTEXT, textA);
5376 expect_textA(hwnd, WM_GETTEXT, textA);
5377 expect_textA(hwnd, EM_GETTEXTEX, textA);
5378 expect_textW(hwnd, EM_GETTEXTEX, textW);
5380 if (em_settextex_supported)
5382 set_textA(hwnd, EM_SETTEXTEX, textA);
5383 expect_textA(hwnd, WM_GETTEXT, textA);
5384 expect_textA(hwnd, EM_GETTEXTEX, textA);
5385 expect_textW(hwnd, EM_GETTEXTEX, textW);
5388 set_textW(hwnd, WM_SETTEXT, textW);
5389 expect_textW(hwnd, WM_GETTEXT, textW);
5390 expect_textA(hwnd, WM_GETTEXT, textA);
5391 expect_textW(hwnd, EM_GETTEXTEX, textW);
5392 expect_textA(hwnd, EM_GETTEXTEX, textA);
5394 if (em_settextex_supported)
5396 set_textW(hwnd, EM_SETTEXTEX, textW);
5397 expect_textW(hwnd, WM_GETTEXT, textW);
5398 expect_textA(hwnd, WM_GETTEXT, textA);
5399 expect_textW(hwnd, EM_GETTEXTEX, textW);
5400 expect_textA(hwnd, EM_GETTEXTEX, textA);
5402 DestroyWindow(hwnd);
5405 static void test_WM_CHAR(void)
5407 HWND hwnd;
5408 int ret;
5409 const char * char_list = "abc\rabc\r";
5410 const char * expected_content_single = "abcabc";
5411 const char * expected_content_multi = "abc\r\nabc\r\n";
5412 char buffer[64] = {0};
5413 const char * p;
5415 /* single-line control must IGNORE carriage returns */
5416 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5417 0, 0, 200, 60, 0, 0, 0, 0);
5418 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5420 p = char_list;
5421 while (*p != '\0') {
5422 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5423 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5424 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5425 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5426 p++;
5429 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5430 ret = strcmp(buffer, expected_content_single);
5431 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5433 DestroyWindow(hwnd);
5435 /* multi-line control inserts CR normally */
5436 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5437 0, 0, 200, 60, 0, 0, 0, 0);
5438 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5440 p = char_list;
5441 while (*p != '\0') {
5442 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5443 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5444 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5445 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5446 p++;
5449 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5450 ret = strcmp(buffer, expected_content_multi);
5451 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5453 DestroyWindow(hwnd);
5456 static void test_EM_GETTEXTLENGTHEX(void)
5458 HWND hwnd;
5459 GETTEXTLENGTHEX gtl;
5460 int ret;
5461 const char * base_string = "base string";
5462 const char * test_string = "a\nb\n\n\r\n";
5463 const char * test_string_after = "a";
5464 const char * test_string_2 = "a\rtest\rstring";
5465 char buffer[64] = {0};
5467 /* single line */
5468 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5469 0, 0, 200, 60, 0, 0, 0, 0);
5470 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5472 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5473 gtl.codepage = CP_ACP;
5474 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5475 ok(ret == 0, "ret %d\n",ret);
5477 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5478 gtl.codepage = CP_ACP;
5479 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5480 ok(ret == 0, "ret %d\n",ret);
5482 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5484 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5485 gtl.codepage = CP_ACP;
5486 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5487 ok(ret == strlen(base_string), "ret %d\n",ret);
5489 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5490 gtl.codepage = CP_ACP;
5491 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5492 ok(ret == strlen(base_string), "ret %d\n",ret);
5494 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5496 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5497 gtl.codepage = CP_ACP;
5498 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5499 ok(ret == 1, "ret %d\n",ret);
5501 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5502 gtl.codepage = CP_ACP;
5503 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5504 ok(ret == 1, "ret %d\n",ret);
5506 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5507 ret = strcmp(buffer, test_string_after);
5508 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5510 DestroyWindow(hwnd);
5512 /* multi line */
5513 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5514 0, 0, 200, 60, 0, 0, 0, 0);
5515 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5517 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5518 gtl.codepage = CP_ACP;
5519 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5520 ok(ret == 0, "ret %d\n",ret);
5522 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5523 gtl.codepage = CP_ACP;
5524 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5525 ok(ret == 0, "ret %d\n",ret);
5527 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5529 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5530 gtl.codepage = CP_ACP;
5531 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5532 ok(ret == strlen(base_string), "ret %d\n",ret);
5534 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5535 gtl.codepage = CP_ACP;
5536 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5537 ok(ret == strlen(base_string), "ret %d\n",ret);
5539 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5541 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5542 gtl.codepage = CP_ACP;
5543 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5544 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5546 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5547 gtl.codepage = CP_ACP;
5548 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5549 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5551 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5553 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5554 gtl.codepage = CP_ACP;
5555 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5556 ok(ret == 10, "ret %d\n",ret);
5558 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5559 gtl.codepage = CP_ACP;
5560 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5561 ok(ret == 6, "ret %d\n",ret);
5563 /* Unicode/NUMCHARS/NUMBYTES */
5564 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5566 gtl.flags = GTL_DEFAULT;
5567 gtl.codepage = 1200;
5568 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5569 ok(ret == lstrlen(test_string_2),
5570 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlen(test_string_2));
5572 gtl.flags = GTL_NUMCHARS;
5573 gtl.codepage = 1200;
5574 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5575 ok(ret == lstrlen(test_string_2),
5576 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlen(test_string_2));
5578 gtl.flags = GTL_NUMBYTES;
5579 gtl.codepage = 1200;
5580 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5581 ok(ret == lstrlen(test_string_2)*2,
5582 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5584 gtl.flags = GTL_PRECISE;
5585 gtl.codepage = 1200;
5586 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5587 ok(ret == lstrlen(test_string_2)*2,
5588 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5590 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5591 gtl.codepage = 1200;
5592 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5593 ok(ret == lstrlen(test_string_2),
5594 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2));
5596 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5597 gtl.codepage = 1200;
5598 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5599 ok(ret == E_INVALIDARG,
5600 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5602 DestroyWindow(hwnd);
5606 /* globals that parent and child access when checking event masks & notifications */
5607 static HWND eventMaskEditHwnd = 0;
5608 static int queriedEventMask;
5609 static int watchForEventMask = 0;
5611 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5612 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5614 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5616 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5618 return DefWindowProcA(hwnd, message, wParam, lParam);
5621 /* test event masks in combination with WM_COMMAND */
5622 static void test_eventMask(void)
5624 HWND parent;
5625 int ret, style;
5626 WNDCLASSA cls;
5627 const char text[] = "foo bar\n";
5628 int eventMask;
5630 /* register class to capture WM_COMMAND */
5631 cls.style = 0;
5632 cls.lpfnWndProc = ParentMsgCheckProcA;
5633 cls.cbClsExtra = 0;
5634 cls.cbWndExtra = 0;
5635 cls.hInstance = GetModuleHandleA(0);
5636 cls.hIcon = 0;
5637 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5638 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5639 cls.lpszMenuName = NULL;
5640 cls.lpszClassName = "EventMaskParentClass";
5641 if(!RegisterClassA(&cls)) assert(0);
5643 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5644 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5645 ok (parent != 0, "Failed to create parent window\n");
5647 eventMaskEditHwnd = new_richedit(parent);
5648 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5650 eventMask = ENM_CHANGE | ENM_UPDATE;
5651 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5652 ok(ret == ENM_NONE, "wrong event mask\n");
5653 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5654 ok(ret == eventMask, "failed to set event mask\n");
5656 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5657 queriedEventMask = 0; /* initialize to something other than we expect */
5658 watchForEventMask = EN_CHANGE;
5659 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5660 ok(ret == TRUE, "failed to set text\n");
5661 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5662 notification in response to WM_SETTEXT */
5663 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5664 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5666 /* check to see if EN_CHANGE is sent when redraw is turned off */
5667 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5668 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5669 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5670 /* redraw is disabled by making the window invisible. */
5671 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5672 queriedEventMask = 0; /* initialize to something other than we expect */
5673 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5674 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5675 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5676 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5677 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5679 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5680 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5681 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5682 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5683 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5684 watchForEventMask = EN_UPDATE;
5685 queriedEventMask = 0; /* initialize to something other than we expect */
5686 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5687 ok(queriedEventMask == 0,
5688 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5689 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5690 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5691 queriedEventMask = 0; /* initialize to something other than we expect */
5692 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5693 ok(queriedEventMask == eventMask,
5694 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5697 DestroyWindow(parent);
5700 static int received_WM_NOTIFY = 0;
5701 static int modify_at_WM_NOTIFY = 0;
5702 static BOOL filter_on_WM_NOTIFY = FALSE;
5703 static HWND hwndRichedit_WM_NOTIFY;
5705 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5707 if(message == WM_NOTIFY)
5709 received_WM_NOTIFY = 1;
5710 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5711 if (filter_on_WM_NOTIFY) return TRUE;
5713 return DefWindowProcA(hwnd, message, wParam, lParam);
5716 static void test_WM_NOTIFY(void)
5718 HWND parent;
5719 WNDCLASSA cls;
5720 CHARFORMAT2 cf2;
5721 int sel_start, sel_end;
5723 /* register class to capture WM_NOTIFY */
5724 cls.style = 0;
5725 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5726 cls.cbClsExtra = 0;
5727 cls.cbWndExtra = 0;
5728 cls.hInstance = GetModuleHandleA(0);
5729 cls.hIcon = 0;
5730 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5731 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5732 cls.lpszMenuName = NULL;
5733 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5734 if(!RegisterClassA(&cls)) assert(0);
5736 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5737 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5738 ok (parent != 0, "Failed to create parent window\n");
5740 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5741 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5743 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5745 /* Notifications for selection change should only be sent when selection
5746 actually changes. EM_SETCHARFORMAT is one message that calls
5747 ME_CommitUndo, which should check whether message should be sent */
5748 received_WM_NOTIFY = 0;
5749 cf2.cbSize = sizeof(CHARFORMAT2);
5750 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5751 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5752 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5753 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5754 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5756 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5757 already at 0. */
5758 received_WM_NOTIFY = 0;
5759 modify_at_WM_NOTIFY = 0;
5760 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5761 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5762 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5764 received_WM_NOTIFY = 0;
5765 modify_at_WM_NOTIFY = 0;
5766 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5767 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5769 received_WM_NOTIFY = 0;
5770 modify_at_WM_NOTIFY = 0;
5771 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5772 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5773 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5775 /* Test for WM_NOTIFY messages with redraw disabled. */
5776 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5777 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5778 received_WM_NOTIFY = 0;
5779 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5780 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5781 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5783 /* Test filtering key events. */
5784 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5785 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5786 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5787 received_WM_NOTIFY = 0;
5788 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5789 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5790 ok(sel_start == 1 && sel_end == 1,
5791 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5792 filter_on_WM_NOTIFY = TRUE;
5793 received_WM_NOTIFY = 0;
5794 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5795 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5796 ok(sel_start == 1 && sel_end == 1,
5797 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5799 /* test with owner set to NULL */
5800 SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5801 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5802 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5803 ok(sel_start == 1 && sel_end == 1,
5804 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5806 DestroyWindow(hwndRichedit_WM_NOTIFY);
5807 DestroyWindow(parent);
5810 static void test_undo_coalescing(void)
5812 HWND hwnd;
5813 int result;
5814 char buffer[64] = {0};
5816 /* multi-line control inserts CR normally */
5817 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5818 0, 0, 200, 60, 0, 0, 0, 0);
5819 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5821 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5822 ok (result == FALSE, "Can undo after window creation.\n");
5823 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5824 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5825 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5826 ok (result == FALSE, "Can redo after window creation.\n");
5827 result = SendMessage(hwnd, EM_REDO, 0, 0);
5828 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5830 /* Test the effect of arrows keys during typing on undo transactions*/
5831 simulate_typing_characters(hwnd, "one two three");
5832 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5833 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5834 simulate_typing_characters(hwnd, " four five six");
5836 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5837 ok (result == FALSE, "Can redo before anything is undone.\n");
5838 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5839 ok (result == TRUE, "Cannot undo typed characters.\n");
5840 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5841 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5842 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5843 ok (result == TRUE, "Cannot redo after undo.\n");
5844 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5845 result = strcmp(buffer, "one two three");
5846 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5848 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5849 ok (result == TRUE, "Cannot undo typed characters.\n");
5850 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5851 ok (result == TRUE, "Failed to undo typed characters.\n");
5852 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5853 result = strcmp(buffer, "");
5854 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5856 /* Test the effect of focus changes during typing on undo transactions*/
5857 simulate_typing_characters(hwnd, "one two three");
5858 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5859 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5860 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5861 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5862 simulate_typing_characters(hwnd, " four five six");
5863 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5864 ok (result == TRUE, "Failed to undo typed characters.\n");
5865 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5866 result = strcmp(buffer, "one two three");
5867 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5869 /* Test the effect of the back key during typing on undo transactions */
5870 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5871 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5872 ok (result == TRUE, "Failed to clear the text.\n");
5873 simulate_typing_characters(hwnd, "one two threa");
5874 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5875 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5876 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5877 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5878 simulate_typing_characters(hwnd, "e four five six");
5879 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5880 ok (result == TRUE, "Failed to undo typed characters.\n");
5881 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5882 result = strcmp(buffer, "");
5883 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5885 /* Test the effect of the delete key during typing on undo transactions */
5886 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5887 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5888 ok(result == TRUE, "Failed to set the text.\n");
5889 SendMessage(hwnd, EM_SETSEL, 1, 1);
5890 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5891 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5892 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5893 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5894 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5895 ok (result == TRUE, "Failed to undo typed characters.\n");
5896 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5897 result = strcmp(buffer, "acd");
5898 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5899 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5900 ok (result == TRUE, "Failed to undo typed characters.\n");
5901 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5902 result = strcmp(buffer, "abcd");
5903 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5905 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5906 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5907 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5908 ok (result == TRUE, "Failed to clear the text.\n");
5909 simulate_typing_characters(hwnd, "one two three");
5910 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5911 ok (result == 0, "expected %d but got %d\n", 0, result);
5912 simulate_typing_characters(hwnd, " four five six");
5913 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5914 ok (result == TRUE, "Failed to undo typed characters.\n");
5915 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5916 result = strcmp(buffer, "one two three");
5917 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5918 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5919 ok (result == TRUE, "Failed to undo typed characters.\n");
5920 ok (result == TRUE, "Failed to undo typed characters.\n");
5921 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5922 result = strcmp(buffer, "");
5923 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5925 DestroyWindow(hwnd);
5928 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5930 int length;
5932 /* MSDN lied, length is actually the number of bytes. */
5933 length = bytes / sizeof(WCHAR);
5934 switch(code)
5936 case WB_ISDELIMITER:
5937 return text[pos] == 'X';
5938 case WB_LEFT:
5939 case WB_MOVEWORDLEFT:
5940 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5941 return pos-1;
5942 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5943 case WB_LEFTBREAK:
5944 pos--;
5945 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5946 pos--;
5947 return pos;
5948 case WB_RIGHT:
5949 case WB_MOVEWORDRIGHT:
5950 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5951 return pos+1;
5952 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5953 case WB_RIGHTBREAK:
5954 pos++;
5955 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5956 pos++;
5957 return pos;
5958 default:
5959 ok(FALSE, "Unexpected code %d\n", code);
5960 break;
5962 return 0;
5965 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5966 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5968 static void test_word_movement(void)
5970 HWND hwnd;
5971 int result;
5972 int sel_start, sel_end;
5973 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5975 /* multi-line control inserts CR normally */
5976 hwnd = new_richedit(NULL);
5978 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5979 ok (result == TRUE, "Failed to clear the text.\n");
5980 SendMessage(hwnd, EM_SETSEL, 0, 0);
5981 /* |one two three */
5983 SEND_CTRL_RIGHT(hwnd);
5984 /* one |two three */
5985 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5986 ok(sel_start == sel_end, "Selection should be empty\n");
5987 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5989 SEND_CTRL_RIGHT(hwnd);
5990 /* one two |three */
5991 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5992 ok(sel_start == sel_end, "Selection should be empty\n");
5993 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5995 SEND_CTRL_LEFT(hwnd);
5996 /* one |two three */
5997 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5998 ok(sel_start == sel_end, "Selection should be empty\n");
5999 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6001 SEND_CTRL_LEFT(hwnd);
6002 /* |one two three */
6003 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6004 ok(sel_start == sel_end, "Selection should be empty\n");
6005 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6007 SendMessage(hwnd, EM_SETSEL, 8, 8);
6008 /* one two | three */
6009 SEND_CTRL_RIGHT(hwnd);
6010 /* one two |three */
6011 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6012 ok(sel_start == sel_end, "Selection should be empty\n");
6013 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6015 SendMessage(hwnd, EM_SETSEL, 11, 11);
6016 /* one two th|ree */
6017 SEND_CTRL_LEFT(hwnd);
6018 /* one two |three */
6019 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6020 ok(sel_start == sel_end, "Selection should be empty\n");
6021 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6023 /* Test with a custom word break procedure that uses X as the delimiter. */
6024 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6025 ok (result == TRUE, "Failed to clear the text.\n");
6026 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6027 /* |one twoXthree */
6028 SEND_CTRL_RIGHT(hwnd);
6029 /* one twoX|three */
6030 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6031 ok(sel_start == sel_end, "Selection should be empty\n");
6032 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6034 DestroyWindow(hwnd);
6036 /* Make sure the behaviour is the same with a unicode richedit window,
6037 * and using unicode functions. */
6039 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6040 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6041 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6043 /* Test with a custom word break procedure that uses X as the delimiter. */
6044 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6045 ok (result == TRUE, "Failed to clear the text.\n");
6046 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6047 /* |one twoXthree */
6048 SEND_CTRL_RIGHT(hwnd);
6049 /* one twoX|three */
6050 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6051 ok(sel_start == sel_end, "Selection should be empty\n");
6052 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6054 DestroyWindow(hwnd);
6057 static void test_EM_CHARFROMPOS(void)
6059 HWND hwnd;
6060 int result;
6061 RECT rcClient;
6062 POINTL point;
6063 point.x = 0;
6064 point.y = 40;
6066 /* multi-line control inserts CR normally */
6067 hwnd = new_richedit(NULL);
6068 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6069 (LPARAM)"one two three four five six seven\reight");
6070 ok(result == 1, "Expected 1, got %d\n", result);
6071 GetClientRect(hwnd, &rcClient);
6073 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6074 ok(result == 34, "expected character index of 34 but got %d\n", result);
6076 /* Test with points outside the bounds of the richedit control. */
6077 point.x = -1;
6078 point.y = 40;
6079 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6080 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6082 point.x = 1000;
6083 point.y = 0;
6084 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6085 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6087 point.x = 1000;
6088 point.y = 36;
6089 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6090 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6092 point.x = 1000;
6093 point.y = -1;
6094 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6095 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6097 point.x = 1000;
6098 point.y = rcClient.bottom + 1;
6099 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6100 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6102 point.x = 1000;
6103 point.y = rcClient.bottom;
6104 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6105 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6107 DestroyWindow(hwnd);
6110 static void test_word_wrap(void)
6112 HWND hwnd;
6113 POINTL point = {0, 60}; /* This point must be below the first line */
6114 const char *text = "Must be long enough to test line wrapping";
6115 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6116 int res, pos, lines;
6118 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6119 * when specified on window creation and set later. */
6120 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6121 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6122 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6123 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6124 ok(res, "WM_SETTEXT failed.\n");
6125 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6126 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6127 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6128 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6130 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6131 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6132 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6133 DestroyWindow(hwnd);
6135 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6136 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6137 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6139 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6140 ok(res, "WM_SETTEXT failed.\n");
6141 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6142 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6143 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6144 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6146 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6147 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6148 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6149 DestroyWindow(hwnd);
6151 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6152 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6153 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6154 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6155 ok(res, "WM_SETTEXT failed.\n");
6156 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6157 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6159 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6160 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6161 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6162 DestroyWindow(hwnd);
6164 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6165 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6166 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6167 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6168 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6169 ok(res, "WM_SETTEXT failed.\n");
6170 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6171 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6173 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6174 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6175 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6177 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6178 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6179 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6180 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6181 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6183 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6184 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6185 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6186 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6187 DestroyWindow(hwnd);
6189 /* Test to see if wrapping happens with redraw disabled. */
6190 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6191 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6192 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6193 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6194 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6195 ok(res, "EM_REPLACESEL failed.\n");
6196 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6197 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6198 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6199 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6200 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6202 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6203 DestroyWindow(hwnd);
6206 static void test_autoscroll(void)
6208 HWND hwnd = new_richedit(NULL);
6209 int lines, ret, redraw;
6210 POINT pt;
6212 for (redraw = 0; redraw <= 1; redraw++) {
6213 trace("testing with WM_SETREDRAW=%d\n", redraw);
6214 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6215 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6216 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6217 ok(lines == 8, "%d lines instead of 8\n", lines);
6218 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6219 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6220 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6221 ret = GetWindowLong(hwnd, GWL_STYLE);
6222 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6224 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6225 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6226 ok(lines == 1, "%d lines instead of 1\n", lines);
6227 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6228 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6229 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6230 ret = GetWindowLong(hwnd, GWL_STYLE);
6231 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6234 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6235 DestroyWindow(hwnd);
6237 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6238 * auto vertical/horizontal scrolling options. */
6239 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6240 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6241 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6242 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6243 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6244 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6245 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6246 ret = GetWindowLong(hwnd, GWL_STYLE);
6247 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6248 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6249 DestroyWindow(hwnd);
6251 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6252 WS_POPUP|ES_MULTILINE,
6253 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6254 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6255 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6256 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6257 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6258 ret = GetWindowLong(hwnd, GWL_STYLE);
6259 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6260 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6261 DestroyWindow(hwnd);
6265 static void test_format_rect(void)
6267 HWND hwnd;
6268 RECT rc, expected, clientRect;
6269 int n;
6270 DWORD options;
6272 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6273 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6274 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6275 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6277 GetClientRect(hwnd, &clientRect);
6279 expected = clientRect;
6280 expected.left += 1;
6281 expected.right -= 1;
6282 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6283 ok(rc.top == expected.top && rc.left == expected.left &&
6284 rc.bottom == expected.bottom && rc.right == expected.right,
6285 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6286 rc.top, rc.left, rc.bottom, rc.right,
6287 expected.top, expected.left, expected.bottom, expected.right);
6289 for (n = -3; n <= 3; n++)
6291 rc = clientRect;
6292 rc.top += n;
6293 rc.left += n;
6294 rc.bottom -= n;
6295 rc.right -= n;
6296 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6298 expected = rc;
6299 expected.top = max(0, rc.top);
6300 expected.left = max(0, rc.left);
6301 expected.bottom = min(clientRect.bottom, rc.bottom);
6302 expected.right = min(clientRect.right, rc.right);
6303 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6304 ok(rc.top == expected.top && rc.left == expected.left &&
6305 rc.bottom == expected.bottom && rc.right == expected.right,
6306 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6307 n, rc.top, rc.left, rc.bottom, rc.right,
6308 expected.top, expected.left, expected.bottom, expected.right);
6311 rc = clientRect;
6312 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6313 expected = clientRect;
6314 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6315 ok(rc.top == expected.top && rc.left == expected.left &&
6316 rc.bottom == expected.bottom && rc.right == expected.right,
6317 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6318 rc.top, rc.left, rc.bottom, rc.right,
6319 expected.top, expected.left, expected.bottom, expected.right);
6321 /* Adding the selectionbar adds the selectionbar width to the left side. */
6322 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6323 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6324 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6325 expected.left += 8; /* selection bar width */
6326 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6327 ok(rc.top == expected.top && rc.left == expected.left &&
6328 rc.bottom == expected.bottom && rc.right == expected.right,
6329 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6330 rc.top, rc.left, rc.bottom, rc.right,
6331 expected.top, expected.left, expected.bottom, expected.right);
6333 rc = clientRect;
6334 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6335 expected = clientRect;
6336 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6337 ok(rc.top == expected.top && rc.left == expected.left &&
6338 rc.bottom == expected.bottom && rc.right == expected.right,
6339 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6340 rc.top, rc.left, rc.bottom, rc.right,
6341 expected.top, expected.left, expected.bottom, expected.right);
6343 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6344 * even if the left side is already 0. */
6345 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6346 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6347 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6348 expected.left -= 8; /* selection bar width */
6349 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6350 ok(rc.top == expected.top && rc.left == expected.left &&
6351 rc.bottom == expected.bottom && rc.right == expected.right,
6352 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6353 rc.top, rc.left, rc.bottom, rc.right,
6354 expected.top, expected.left, expected.bottom, expected.right);
6356 /* Set the absolute value of the formatting rectangle. */
6357 rc = clientRect;
6358 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6359 expected = clientRect;
6360 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6361 ok(rc.top == expected.top && rc.left == expected.left &&
6362 rc.bottom == expected.bottom && rc.right == expected.right,
6363 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6364 n, rc.top, rc.left, rc.bottom, rc.right,
6365 expected.top, expected.left, expected.bottom, expected.right);
6367 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6368 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6369 * tests show that this isn't true. */
6370 rc.top = 15;
6371 rc.left = 15;
6372 rc.bottom = clientRect.bottom - 15;
6373 rc.right = clientRect.right - 15;
6374 expected = rc;
6375 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6376 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6377 ok(rc.top == expected.top && rc.left == expected.left &&
6378 rc.bottom == expected.bottom && rc.right == expected.right,
6379 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6380 rc.top, rc.left, rc.bottom, rc.right,
6381 expected.top, expected.left, expected.bottom, expected.right);
6383 /* For some reason it does not limit the values to the client rect with
6384 * a WPARAM value of 1. */
6385 rc.top = -15;
6386 rc.left = -15;
6387 rc.bottom = clientRect.bottom + 15;
6388 rc.right = clientRect.right + 15;
6389 expected = rc;
6390 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6391 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6392 ok(rc.top == expected.top && rc.left == expected.left &&
6393 rc.bottom == expected.bottom && rc.right == expected.right,
6394 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6395 rc.top, rc.left, rc.bottom, rc.right,
6396 expected.top, expected.left, expected.bottom, expected.right);
6398 DestroyWindow(hwnd);
6400 /* The extended window style affects the formatting rectangle. */
6401 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6402 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6403 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6404 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6406 GetClientRect(hwnd, &clientRect);
6408 expected = clientRect;
6409 expected.left += 1;
6410 expected.top += 1;
6411 expected.right -= 1;
6412 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6413 ok(rc.top == expected.top && rc.left == expected.left &&
6414 rc.bottom == expected.bottom && rc.right == expected.right,
6415 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6416 rc.top, rc.left, rc.bottom, rc.right,
6417 expected.top, expected.left, expected.bottom, expected.right);
6419 rc = clientRect;
6420 rc.top += 5;
6421 rc.left += 5;
6422 rc.bottom -= 5;
6423 rc.right -= 5;
6424 expected = rc;
6425 expected.top -= 1;
6426 expected.left -= 1;
6427 expected.right += 1;
6428 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6429 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6430 ok(rc.top == expected.top && rc.left == expected.left &&
6431 rc.bottom == expected.bottom && rc.right == expected.right,
6432 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6433 rc.top, rc.left, rc.bottom, rc.right,
6434 expected.top, expected.left, expected.bottom, expected.right);
6436 DestroyWindow(hwnd);
6439 static void test_WM_GETDLGCODE(void)
6441 HWND hwnd;
6442 UINT res, expected;
6443 MSG msg;
6445 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6447 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6448 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6449 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6450 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6451 msg.hwnd = hwnd;
6452 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6453 expected = expected | DLGC_WANTMESSAGE;
6454 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6455 res, expected);
6456 DestroyWindow(hwnd);
6458 msg.message = WM_KEYDOWN;
6459 msg.wParam = VK_RETURN;
6460 msg.lParam = (MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6461 msg.pt.x = 0;
6462 msg.pt.y = 0;
6463 msg.time = GetTickCount();
6465 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6466 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6467 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6468 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6469 msg.hwnd = hwnd;
6470 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6471 expected = expected | DLGC_WANTMESSAGE;
6472 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6473 res, expected);
6474 DestroyWindow(hwnd);
6476 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6477 ES_MULTILINE|WS_POPUP,
6478 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6479 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6480 msg.hwnd = hwnd;
6481 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6482 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6483 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6484 res, expected);
6485 DestroyWindow(hwnd);
6487 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6488 ES_WANTRETURN|WS_POPUP,
6489 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6490 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6491 msg.hwnd = hwnd;
6492 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6493 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6494 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6495 res, expected);
6496 DestroyWindow(hwnd);
6498 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6499 WS_POPUP,
6500 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6501 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6502 msg.hwnd = hwnd;
6503 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6504 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6505 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6506 res, expected);
6507 DestroyWindow(hwnd);
6509 msg.wParam = VK_TAB;
6510 msg.lParam = (MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6512 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6513 ES_MULTILINE|WS_POPUP,
6514 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6515 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6516 msg.hwnd = hwnd;
6517 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6518 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6519 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6520 res, expected);
6521 DestroyWindow(hwnd);
6523 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6524 WS_POPUP,
6525 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6526 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6527 msg.hwnd = hwnd;
6528 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6529 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6530 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6531 res, expected);
6532 DestroyWindow(hwnd);
6534 hold_key(VK_CONTROL);
6536 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6537 ES_MULTILINE|WS_POPUP,
6538 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6539 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6540 msg.hwnd = hwnd;
6541 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6542 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6543 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6544 res, expected);
6545 DestroyWindow(hwnd);
6547 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6548 WS_POPUP,
6549 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6550 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6551 msg.hwnd = hwnd;
6552 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6553 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6554 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6555 res, expected);
6556 DestroyWindow(hwnd);
6558 release_key(VK_CONTROL);
6560 msg.wParam = 'a';
6561 msg.lParam = (MapVirtualKey('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6563 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6564 ES_MULTILINE|WS_POPUP,
6565 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6566 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6567 msg.hwnd = hwnd;
6568 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6569 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6570 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6571 res, expected);
6572 DestroyWindow(hwnd);
6574 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6575 WS_POPUP,
6576 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6577 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6578 msg.hwnd = hwnd;
6579 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6580 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6581 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6582 res, expected);
6583 DestroyWindow(hwnd);
6585 msg.message = WM_CHAR;
6587 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6588 ES_MULTILINE|WS_POPUP,
6589 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6590 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6591 msg.hwnd = hwnd;
6592 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6593 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6594 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6595 res, expected);
6596 DestroyWindow(hwnd);
6598 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6599 WS_POPUP,
6600 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6601 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6602 msg.hwnd = hwnd;
6603 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6604 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6605 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6606 res, expected);
6607 DestroyWindow(hwnd);
6610 static void test_zoom(void)
6612 HWND hwnd;
6613 UINT ret;
6614 RECT rc;
6615 POINT pt;
6616 int numerator, denominator;
6618 hwnd = new_richedit(NULL);
6619 GetClientRect(hwnd, &rc);
6620 pt.x = (rc.right - rc.left) / 2;
6621 pt.y = (rc.bottom - rc.top) / 2;
6622 ClientToScreen(hwnd, &pt);
6624 /* Test initial zoom value */
6625 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6626 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6627 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6628 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6630 /* test scroll wheel */
6631 hold_key(VK_CONTROL);
6632 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6633 MAKELPARAM(pt.x, pt.y));
6634 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6635 release_key(VK_CONTROL);
6637 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6638 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6639 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6640 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6642 /* Test how much the mouse wheel can zoom in and out. */
6643 ret = SendMessage(hwnd, EM_SETZOOM, 490, 100);
6644 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6646 hold_key(VK_CONTROL);
6647 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6648 MAKELPARAM(pt.x, pt.y));
6649 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6650 release_key(VK_CONTROL);
6652 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6653 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6654 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6655 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6657 ret = SendMessage(hwnd, EM_SETZOOM, 491, 100);
6658 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6660 hold_key(VK_CONTROL);
6661 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6662 MAKELPARAM(pt.x, pt.y));
6663 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6664 release_key(VK_CONTROL);
6666 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6667 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6668 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6669 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6671 ret = SendMessage(hwnd, EM_SETZOOM, 20, 100);
6672 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6674 hold_key(VK_CONTROL);
6675 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6676 MAKELPARAM(pt.x, pt.y));
6677 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6678 release_key(VK_CONTROL);
6680 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6681 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6682 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6683 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6685 ret = SendMessage(hwnd, EM_SETZOOM, 19, 100);
6686 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6688 hold_key(VK_CONTROL);
6689 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6690 MAKELPARAM(pt.x, pt.y));
6691 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6692 release_key(VK_CONTROL);
6694 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6695 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6696 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6697 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6699 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6700 ret = SendMessage(hwnd, EM_SETZOOM, 50, 13);
6701 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6703 hold_key(VK_CONTROL);
6704 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6705 MAKELPARAM(pt.x, pt.y));
6706 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6707 release_key(VK_CONTROL);
6709 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6710 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6711 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6712 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6714 /* Test bounds checking on EM_SETZOOM */
6715 ret = SendMessage(hwnd, EM_SETZOOM, 2, 127);
6716 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6718 ret = SendMessage(hwnd, EM_SETZOOM, 127, 2);
6719 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6721 ret = SendMessage(hwnd, EM_SETZOOM, 2, 128);
6722 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6724 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6725 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6726 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6727 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6729 ret = SendMessage(hwnd, EM_SETZOOM, 128, 2);
6730 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6732 /* See if negative numbers are accepted. */
6733 ret = SendMessage(hwnd, EM_SETZOOM, -100, -100);
6734 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6736 /* See if negative numbers are accepted. */
6737 ret = SendMessage(hwnd, EM_SETZOOM, 0, 100);
6738 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6740 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6741 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6742 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6743 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6745 /* Reset the zoom value */
6746 ret = SendMessage(hwnd, EM_SETZOOM, 0, 0);
6747 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6749 DestroyWindow(hwnd);
6752 struct dialog_mode_messages
6754 int wm_getdefid, wm_close, wm_nextdlgctl;
6757 static struct dialog_mode_messages dm_messages;
6759 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6760 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6761 "got %d\n", wmclose, dm_messages.wm_close); \
6762 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6763 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6764 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6765 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6767 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6769 switch (iMsg)
6771 case DM_GETDEFID:
6772 dm_messages.wm_getdefid++;
6773 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6774 case WM_NEXTDLGCTL:
6775 dm_messages.wm_nextdlgctl++;
6776 break;
6777 case WM_CLOSE:
6778 dm_messages.wm_close++;
6779 break;
6782 return DefWindowProc(hwnd, iMsg, wParam, lParam);
6785 static void test_dialogmode(void)
6787 HWND hwRichEdit, hwParent, hwButton;
6788 MSG msg= {0};
6789 int lcount, r;
6790 WNDCLASSA cls;
6792 cls.style = 0;
6793 cls.lpfnWndProc = dialog_mode_wnd_proc;
6794 cls.cbClsExtra = 0;
6795 cls.cbWndExtra = 0;
6796 cls.hInstance = GetModuleHandleA(0);
6797 cls.hIcon = 0;
6798 cls.hCursor = LoadCursorA(0, IDC_ARROW);
6799 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6800 cls.lpszMenuName = NULL;
6801 cls.lpszClassName = "DialogModeParentClass";
6802 if(!RegisterClassA(&cls)) assert(0);
6804 hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6805 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6807 /* Test richedit(ES_MULTILINE) */
6809 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6811 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6812 ok(0 == r, "expected 0, got %d\n", r);
6813 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6814 ok(2 == lcount, "expected 2, got %d\n", lcount);
6816 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6817 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6819 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6820 ok(0 == r, "expected 0, got %d\n", r);
6821 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6822 ok(3 == lcount, "expected 3, got %d\n", lcount);
6824 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6825 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6826 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6827 ok(0 == r, "expected 0, got %d\n", r);
6828 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6829 ok(3 == lcount, "expected 3, got %d\n", lcount);
6831 DestroyWindow(hwRichEdit);
6833 /* Test standalone richedit(ES_MULTILINE) */
6835 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6837 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6838 ok(0 == r, "expected 0, got %d\n", r);
6839 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6840 ok(2 == lcount, "expected 2, got %d\n", lcount);
6842 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6843 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6845 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6846 ok(0 == r, "expected 0, got %d\n", r);
6847 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6848 ok(2 == lcount, "expected 2, got %d\n", lcount);
6850 DestroyWindow(hwRichEdit);
6852 /* Check a destination for messages */
6854 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6856 SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6857 SetParent( hwRichEdit, NULL);
6859 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6860 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6862 memset(&dm_messages, 0, sizeof(dm_messages));
6863 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6864 ok(0 == r, "expected 0, got %d\n", r);
6865 test_dm_messages(0, 1, 0);
6867 memset(&dm_messages, 0, sizeof(dm_messages));
6868 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6869 ok(0 == r, "expected 0, got %d\n", r);
6870 test_dm_messages(0, 0, 1);
6872 DestroyWindow(hwRichEdit);
6874 /* Check messages from richedit(ES_MULTILINE) */
6876 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6878 memset(&dm_messages, 0, sizeof(dm_messages));
6879 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6880 ok(0 == r, "expected 0, got %d\n", r);
6881 test_dm_messages(0, 0, 0);
6883 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6884 ok(2 == lcount, "expected 2, got %d\n", lcount);
6886 memset(&dm_messages, 0, sizeof(dm_messages));
6887 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6888 ok(0 == r, "expected 0, got %d\n", r);
6889 test_dm_messages(0, 0, 0);
6891 memset(&dm_messages, 0, sizeof(dm_messages));
6892 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6893 ok(0 == r, "expected 0, got %d\n", r);
6894 test_dm_messages(0, 0, 0);
6896 memset(&dm_messages, 0, sizeof(dm_messages));
6897 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6898 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6899 test_dm_messages(0, 0, 0);
6901 memset(&dm_messages, 0, sizeof(dm_messages));
6902 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6903 ok(0 == r, "expected 0, got %d\n", r);
6904 test_dm_messages(0, 1, 0);
6906 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6907 ok(2 == lcount, "expected 2, got %d\n", lcount);
6909 memset(&dm_messages, 0, sizeof(dm_messages));
6910 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6911 ok(0 == r, "expected 0, got %d\n", r);
6912 test_dm_messages(0, 0, 0);
6914 memset(&dm_messages, 0, sizeof(dm_messages));
6915 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6916 ok(0 == r, "expected 0, got %d\n", r);
6917 test_dm_messages(0, 0, 1);
6919 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6920 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6921 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6923 memset(&dm_messages, 0, sizeof(dm_messages));
6924 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6925 ok(0 == r, "expected 0, got %d\n", r);
6926 test_dm_messages(0, 1, 1);
6928 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6929 ok(2 == lcount, "expected 2, got %d\n", lcount);
6931 DestroyWindow(hwButton);
6932 DestroyWindow(hwRichEdit);
6934 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
6936 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, hwParent);
6938 memset(&dm_messages, 0, sizeof(dm_messages));
6939 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6940 ok(0 == r, "expected 0, got %d\n", r);
6941 test_dm_messages(0, 0, 0);
6943 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6944 ok(2 == lcount, "expected 2, got %d\n", lcount);
6946 memset(&dm_messages, 0, sizeof(dm_messages));
6947 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6948 ok(0 == r, "expected 0, got %d\n", r);
6949 test_dm_messages(0, 0, 0);
6951 memset(&dm_messages, 0, sizeof(dm_messages));
6952 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6953 ok(0 == r, "expected 0, got %d\n", r);
6954 test_dm_messages(0, 0, 0);
6956 memset(&dm_messages, 0, sizeof(dm_messages));
6957 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6958 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6959 test_dm_messages(0, 0, 0);
6961 memset(&dm_messages, 0, sizeof(dm_messages));
6962 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6963 ok(0 == r, "expected 0, got %d\n", r);
6964 test_dm_messages(0, 0, 0);
6966 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6967 ok(3 == lcount, "expected 3, got %d\n", lcount);
6969 memset(&dm_messages, 0, sizeof(dm_messages));
6970 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6971 ok(0 == r, "expected 0, got %d\n", r);
6972 test_dm_messages(0, 0, 0);
6974 memset(&dm_messages, 0, sizeof(dm_messages));
6975 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6976 ok(0 == r, "expected 0, got %d\n", r);
6977 test_dm_messages(0, 0, 1);
6979 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6980 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6981 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6983 memset(&dm_messages, 0, sizeof(dm_messages));
6984 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6985 ok(0 == r, "expected 0, got %d\n", r);
6986 test_dm_messages(0, 0, 0);
6988 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6989 ok(4 == lcount, "expected 4, got %d\n", lcount);
6991 DestroyWindow(hwButton);
6992 DestroyWindow(hwRichEdit);
6994 /* Check messages from richedit(0) */
6996 hwRichEdit = new_window(RICHEDIT_CLASS, 0, hwParent);
6998 memset(&dm_messages, 0, sizeof(dm_messages));
6999 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7000 ok(0 == r, "expected 0, got %d\n", r);
7001 test_dm_messages(0, 0, 0);
7003 memset(&dm_messages, 0, sizeof(dm_messages));
7004 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7005 ok(0 == r, "expected 0, got %d\n", r);
7006 test_dm_messages(0, 0, 0);
7008 memset(&dm_messages, 0, sizeof(dm_messages));
7009 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7010 ok(0 == r, "expected 0, got %d\n", r);
7011 test_dm_messages(0, 0, 0);
7013 memset(&dm_messages, 0, sizeof(dm_messages));
7014 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7015 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7016 test_dm_messages(0, 0, 0);
7018 memset(&dm_messages, 0, sizeof(dm_messages));
7019 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7020 ok(0 == r, "expected 0, got %d\n", r);
7021 test_dm_messages(0, 1, 0);
7023 memset(&dm_messages, 0, sizeof(dm_messages));
7024 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7025 ok(0 == r, "expected 0, got %d\n", r);
7026 test_dm_messages(0, 0, 0);
7028 memset(&dm_messages, 0, sizeof(dm_messages));
7029 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7030 ok(0 == r, "expected 0, got %d\n", r);
7031 test_dm_messages(0, 0, 1);
7033 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7034 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7035 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7037 memset(&dm_messages, 0, sizeof(dm_messages));
7038 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7039 ok(0 == r, "expected 0, got %d\n", r);
7040 test_dm_messages(0, 1, 1);
7042 DestroyWindow(hwRichEdit);
7044 /* Check messages from richedit(ES_WANTRETURN) */
7046 hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7048 memset(&dm_messages, 0, sizeof(dm_messages));
7049 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7050 ok(0 == r, "expected 0, got %d\n", r);
7051 test_dm_messages(0, 0, 0);
7053 memset(&dm_messages, 0, sizeof(dm_messages));
7054 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7055 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7056 test_dm_messages(0, 0, 0);
7058 memset(&dm_messages, 0, sizeof(dm_messages));
7059 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7060 ok(0 == r, "expected 0, got %d\n", r);
7061 test_dm_messages(0, 0, 0);
7063 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7064 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7065 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7067 memset(&dm_messages, 0, sizeof(dm_messages));
7068 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7069 ok(0 == r, "expected 0, got %d\n", r);
7070 test_dm_messages(0, 0, 0);
7072 DestroyWindow(hwRichEdit);
7073 DestroyWindow(hwParent);
7076 static void test_EM_FINDWORDBREAK_W(void)
7078 static const struct {
7079 WCHAR c;
7080 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7081 } delimiter_tests[] = {
7082 {0x0a, FALSE}, /* newline */
7083 {0x0b, FALSE}, /* vertical tab */
7084 {0x0c, FALSE}, /* form feed */
7085 {0x0d, FALSE}, /* carriage return */
7086 {0x20, TRUE}, /* space */
7087 {0x61, FALSE}, /* capital letter a */
7088 {0xa0, FALSE}, /* no-break space */
7089 {0x2000, FALSE}, /* en quad */
7090 {0x3000, FALSE}, /* Ideographic space */
7091 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7092 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7093 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7094 {0xac00, FALSE}, /* Hangul character GA*/
7095 {0xd7af, FALSE}, /* End of Hangul character chart */
7096 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7097 {0xff20, FALSE}, /* fullwidth commercial @ */
7098 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7100 int i;
7101 HWND hwndRichEdit = new_richeditW(NULL);
7102 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7103 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7105 WCHAR wbuf[2];
7106 int result;
7108 wbuf[0] = delimiter_tests[i].c;
7109 wbuf[1] = 0;
7110 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7111 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7112 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7113 todo_wine
7114 ok(result == delimiter_tests[i].isdelimiter,
7115 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7116 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7117 else
7118 ok(result == delimiter_tests[i].isdelimiter,
7119 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7120 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7122 DestroyWindow(hwndRichEdit);
7125 static void test_EM_FINDWORDBREAK_A(void)
7127 static const struct {
7128 WCHAR c;
7129 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7130 } delimiter_tests[] = {
7131 {0x0a, FALSE}, /* newline */
7132 {0x0b, FALSE}, /* vertical tab */
7133 {0x0c, FALSE}, /* form feed */
7134 {0x0d, FALSE}, /* carriage return */
7135 {0x20, TRUE}, /* space */
7136 {0x61, FALSE}, /* capital letter a */
7138 int i;
7139 HWND hwndRichEdit = new_richedit(NULL);
7141 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7142 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7144 int result;
7145 char buf[2];
7146 buf[0] = delimiter_tests[i].c;
7147 buf[1] = 0;
7148 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7149 result = SendMessage(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7150 if (buf[0] == 0x20)
7151 todo_wine
7152 ok(result == delimiter_tests[i].isdelimiter,
7153 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7154 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7155 else
7156 ok(result == delimiter_tests[i].isdelimiter,
7157 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7158 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7160 DestroyWindow(hwndRichEdit);
7163 START_TEST( editor )
7165 BOOL ret;
7166 /* Must explicitly LoadLibrary(). The test has no references to functions in
7167 * RICHED20.DLL, so the linker doesn't actually link to it. */
7168 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
7169 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7171 test_WM_CHAR();
7172 test_EM_FINDTEXT();
7173 test_EM_GETLINE();
7174 test_EM_POSFROMCHAR();
7175 test_EM_SCROLLCARET();
7176 test_EM_SCROLL();
7177 test_scrollbar_visibility();
7178 test_WM_SETTEXT();
7179 test_EM_LINELENGTH();
7180 test_EM_SETCHARFORMAT();
7181 test_EM_SETTEXTMODE();
7182 test_TM_PLAINTEXT();
7183 test_EM_SETOPTIONS();
7184 test_WM_GETTEXT();
7185 test_EM_GETTEXTRANGE();
7186 test_EM_GETSELTEXT();
7187 test_EM_SETUNDOLIMIT();
7188 test_ES_PASSWORD();
7189 test_EM_SETTEXTEX();
7190 test_EM_LIMITTEXT();
7191 test_EM_EXLIMITTEXT();
7192 test_EM_GETLIMITTEXT();
7193 test_WM_SETFONT();
7194 test_EM_GETMODIFY();
7195 test_EM_EXSETSEL();
7196 test_WM_PASTE();
7197 test_EM_STREAMIN();
7198 test_EM_STREAMOUT();
7199 test_EM_STREAMOUT_FONTTBL();
7200 test_EM_StreamIn_Undo();
7201 test_EM_FORMATRANGE();
7202 test_unicode_conversions();
7203 test_EM_GETTEXTLENGTHEX();
7204 test_EM_REPLACESEL(1);
7205 test_EM_REPLACESEL(0);
7206 test_WM_NOTIFY();
7207 test_EM_AUTOURLDETECT();
7208 test_eventMask();
7209 test_undo_coalescing();
7210 test_word_movement();
7211 test_EM_CHARFROMPOS();
7212 test_SETPARAFORMAT();
7213 test_word_wrap();
7214 test_autoscroll();
7215 test_format_rect();
7216 test_WM_GETDLGCODE();
7217 test_zoom();
7218 test_dialogmode();
7219 test_EM_FINDWORDBREAK_W();
7220 test_EM_FINDWORDBREAK_A();
7222 /* Set the environment variable WINETEST_RICHED20 to keep windows
7223 * responsive and open for 30 seconds. This is useful for debugging.
7225 if (getenv( "WINETEST_RICHED20" )) {
7226 keep_responsive(30);
7229 OleFlushClipboard();
7230 ret = FreeLibrary(hmoduleRichEdit);
7231 ok(ret, "error: %d\n", (int) GetLastError());