push 553a83299288f61678d62ade87a3a2a5489a8ad8
[wine/hacks.git] / dlls / riched20 / tests / editor.c
blobea94019d96c68bc7f322c3d06df9c9f01077d348
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 <assert.h>
25 #include <windef.h>
26 #include <winbase.h>
27 #include <wingdi.h>
28 #include <winuser.h>
29 #include <winnls.h>
30 #include <ole2.h>
31 #include <richedit.h>
32 #include <time.h>
33 #include <wine/test.h>
35 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
37 #define ok_w3(format, szString1, szString2, szString3) \
38 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
39 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
40 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
41 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString1), \
42 format, string1, string2, string3);
44 static HMODULE hmoduleRichEdit;
46 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
47 HWND hwnd;
48 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
49 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
50 hmoduleRichEdit, NULL);
51 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
52 return hwnd;
55 static HWND new_richedit(HWND parent) {
56 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
59 static void processPendingMessages(void)
61 MSG msg;
62 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
63 TranslateMessage(&msg);
64 DispatchMessage(&msg);
68 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
70 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
71 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
72 SetFocus(hwnd);
73 keybd_event(mod_vk, mod_scan_code, 0, 0);
74 keybd_event(vk, scan_code, 0, 0);
75 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
76 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
77 processPendingMessages();
80 static void simulate_typing_characters(HWND hwnd, const char* szChars)
82 int ret;
84 while (*szChars != '\0') {
85 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
86 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
87 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
88 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
89 szChars++;
93 static const char haystack[] = "WINEWine wineWine wine WineWine";
94 /* ^0 ^10 ^20 ^30 */
96 struct find_s {
97 int start;
98 int end;
99 const char *needle;
100 int flags;
101 int expected_loc;
102 int _todo_wine;
106 struct find_s find_tests[] = {
107 /* Find in empty text */
108 {0, -1, "foo", FR_DOWN, -1, 0},
109 {0, -1, "foo", 0, -1, 0},
110 {0, -1, "", FR_DOWN, -1, 0},
111 {20, 5, "foo", FR_DOWN, -1, 0},
112 {5, 20, "foo", FR_DOWN, -1, 0}
115 struct find_s find_tests2[] = {
116 /* No-result find */
117 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
118 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
120 /* Subsequent finds */
121 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
122 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
123 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
124 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
126 /* Find backwards */
127 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
128 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
129 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
131 /* Case-insensitive */
132 {1, 31, "wInE", FR_DOWN, 4, 0},
133 {1, 31, "Wine", FR_DOWN, 4, 0},
135 /* High-to-low ranges */
136 {20, 5, "Wine", FR_DOWN, -1, 0},
137 {2, 1, "Wine", FR_DOWN, -1, 0},
138 {30, 29, "Wine", FR_DOWN, -1, 0},
139 {20, 5, "Wine", 0, 13, 0},
141 /* Find nothing */
142 {5, 10, "", FR_DOWN, -1, 0},
143 {10, 5, "", FR_DOWN, -1, 0},
144 {0, -1, "", FR_DOWN, -1, 0},
145 {10, 5, "", 0, -1, 0},
147 /* Whole-word search */
148 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
149 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
150 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
151 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
152 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
153 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
154 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
156 /* Bad ranges */
157 {5, 200, "XXX", FR_DOWN, -1, 0},
158 {-20, 20, "Wine", FR_DOWN, -1, 0},
159 {-20, 20, "Wine", FR_DOWN, -1, 0},
160 {-15, -20, "Wine", FR_DOWN, -1, 0},
161 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
163 /* Check the case noted in bug 4479 where matches at end aren't recognized */
164 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
165 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
166 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
167 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
168 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
170 /* The backwards case of bug 4479; bounds look right
171 * Fails because backward find is wrong */
172 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
173 {0, 20, "WINE", FR_MATCHCASE, -1, 0},
175 {0, -1, "wineWine wine", 0, -1, 0},
178 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
179 int findloc;
180 FINDTEXT ft;
181 memset(&ft, 0, sizeof(ft));
182 ft.chrg.cpMin = f->start;
183 ft.chrg.cpMax = f->end;
184 ft.lpstrText = f->needle;
185 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
186 ok(findloc == f->expected_loc,
187 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
188 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
191 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
192 int id) {
193 int findloc;
194 FINDTEXTEX ft;
195 int expected_end_loc;
197 memset(&ft, 0, sizeof(ft));
198 ft.chrg.cpMin = f->start;
199 ft.chrg.cpMax = f->end;
200 ft.lpstrText = f->needle;
201 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
202 ok(findloc == f->expected_loc,
203 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
204 name, id, f->needle, f->start, f->end, f->flags, findloc);
205 ok(ft.chrgText.cpMin == f->expected_loc,
206 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
207 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
208 expected_end_loc = ((f->expected_loc == -1) ? -1
209 : f->expected_loc + strlen(f->needle));
210 ok(ft.chrgText.cpMax == expected_end_loc,
211 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
212 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
215 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
216 int num_tests)
218 int i;
220 for (i = 0; i < num_tests; i++) {
221 if (find[i]._todo_wine) {
222 todo_wine {
223 check_EM_FINDTEXT(hwnd, name, &find[i], i);
224 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
226 } else {
227 check_EM_FINDTEXT(hwnd, name, &find[i], i);
228 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
233 static void test_EM_FINDTEXT(void)
235 HWND hwndRichEdit = new_richedit(NULL);
236 CHARFORMAT2 cf2;
238 /* Empty rich edit control */
239 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
240 sizeof(find_tests)/sizeof(struct find_s));
242 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
244 /* Haystack text */
245 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
246 sizeof(find_tests2)/sizeof(struct find_s));
248 /* Setting a format on an arbitrary range should have no effect in search
249 results. This tests correct offset reporting across runs. */
250 cf2.cbSize = sizeof(CHARFORMAT2);
251 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
252 (LPARAM) &cf2);
253 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
254 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
255 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
256 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
258 /* Haystack text, again */
259 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
260 sizeof(find_tests2)/sizeof(struct find_s));
262 /* Yet another range */
263 cf2.dwMask = CFM_BOLD | cf2.dwMask;
264 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
265 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
266 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
268 /* Haystack text, again */
269 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
270 sizeof(find_tests2)/sizeof(struct find_s));
272 DestroyWindow(hwndRichEdit);
275 static const struct getline_s {
276 int line;
277 size_t buffer_len;
278 const char *text;
279 } gl[] = {
280 {0, 10, "foo bar\r"},
281 {1, 10, "\r"},
282 {2, 10, "bar\r"},
283 {3, 10, "\r"},
285 /* Buffer smaller than line length */
286 {0, 2, "foo bar\r"},
287 {0, 1, "foo bar\r"},
288 {0, 0, "foo bar\r"}
291 static void test_EM_GETLINE(void)
293 int i;
294 HWND hwndRichEdit = new_richedit(NULL);
295 static const int nBuf = 1024;
296 char dest[1024], origdest[1024];
297 const char text[] = "foo bar\n"
298 "\n"
299 "bar\n";
301 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
303 memset(origdest, 0xBB, nBuf);
304 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
306 int nCopied;
307 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
308 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
309 memset(dest, 0xBB, nBuf);
310 *(WORD *) dest = gl[i].buffer_len;
312 /* EM_GETLINE appends a "\r\0" to the end of the line
313 * nCopied counts up to and including the '\r' */
314 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
315 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
316 expected_nCopied);
317 /* two special cases since a parameter is passed via dest */
318 if (gl[i].buffer_len == 0)
319 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
320 "buffer_len=0\n");
321 else if (gl[i].buffer_len == 1)
322 ok(dest[0] == gl[i].text[0] && !dest[1] &&
323 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
324 else
326 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
327 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
328 ok(!strncmp(dest + expected_bytes_written, origdest
329 + expected_bytes_written, nBuf - expected_bytes_written),
330 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
334 DestroyWindow(hwndRichEdit);
337 static void test_EM_LINELENGTH(void)
339 HWND hwndRichEdit = new_richedit(NULL);
340 const char * text =
341 "richedit1\r"
342 "richedit1\n"
343 "richedit1\r\n"
344 "richedit1";
345 int offset_test[10][2] = {
346 {0, 9},
347 {5, 9},
348 {10, 9},
349 {15, 9},
350 {20, 9},
351 {25, 9},
352 {30, 9},
353 {35, 9},
354 {40, 0},
355 {45, 0},
357 int i;
358 LRESULT result;
360 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
362 for (i = 0; i < 10; i++) {
363 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
364 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
365 offset_test[i][0], result, offset_test[i][1]);
368 DestroyWindow(hwndRichEdit);
371 static int get_scroll_pos_y(HWND hwnd)
373 POINT p = {-1, -1};
374 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
375 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
376 return p.y;
379 static void move_cursor(HWND hwnd, long charindex)
381 CHARRANGE cr;
382 cr.cpMax = charindex;
383 cr.cpMin = charindex;
384 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
387 static void line_scroll(HWND hwnd, int amount)
389 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
392 static void test_EM_SCROLLCARET(void)
394 int prevY, curY;
395 HWND hwndRichEdit = new_richedit(NULL);
396 const char text[] = "aa\n"
397 "this is a long line of text that should be longer than the "
398 "control's width\n"
399 "cc\n"
400 "dd\n"
401 "ee\n"
402 "ff\n"
403 "gg\n"
404 "hh\n";
406 /* Can't verify this */
407 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
409 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
411 /* Caret above visible window */
412 line_scroll(hwndRichEdit, 3);
413 prevY = get_scroll_pos_y(hwndRichEdit);
414 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
415 curY = get_scroll_pos_y(hwndRichEdit);
416 ok(prevY != curY, "%d == %d\n", prevY, curY);
418 /* Caret below visible window */
419 move_cursor(hwndRichEdit, sizeof(text) - 1);
420 line_scroll(hwndRichEdit, -3);
421 prevY = get_scroll_pos_y(hwndRichEdit);
422 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
423 curY = get_scroll_pos_y(hwndRichEdit);
424 ok(prevY != curY, "%d == %d\n", prevY, curY);
426 /* Caret in visible window */
427 move_cursor(hwndRichEdit, sizeof(text) - 2);
428 prevY = get_scroll_pos_y(hwndRichEdit);
429 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
430 curY = get_scroll_pos_y(hwndRichEdit);
431 ok(prevY == curY, "%d != %d\n", prevY, curY);
433 /* Caret still in visible window */
434 line_scroll(hwndRichEdit, -1);
435 prevY = get_scroll_pos_y(hwndRichEdit);
436 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
437 curY = get_scroll_pos_y(hwndRichEdit);
438 ok(prevY == curY, "%d != %d\n", prevY, curY);
440 DestroyWindow(hwndRichEdit);
443 static void test_EM_POSFROMCHAR(void)
445 HWND hwndRichEdit = new_richedit(NULL);
446 int i;
447 LRESULT result;
448 unsigned int height = 0;
449 int xpos = 0;
450 static const char text[] = "aa\n"
451 "this is a long line of text that should be longer than the "
452 "control's width\n"
453 "cc\n"
454 "dd\n"
455 "ee\n"
456 "ff\n"
457 "gg\n"
458 "hh\n";
460 /* Fill the control to lines to ensure that most of them are offscreen */
461 for (i = 0; i < 50; i++)
463 /* Do not modify the string; it is exactly 16 characters long. */
464 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
465 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
469 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
470 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
471 Richedit 3.0 accepts either of the above API conventions.
474 /* Testing Richedit 2.0 API format */
476 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
477 Since all lines are identical and drawn with the same font,
478 they should have the same height... right?
480 for (i = 0; i < 50; i++)
482 /* All the lines are 16 characters long */
483 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
484 if (i == 0)
486 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
487 todo_wine {
488 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
490 xpos = LOWORD(result);
492 else if (i == 1)
494 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
495 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
496 height = HIWORD(result);
498 else
500 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
501 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
505 /* Testing position at end of text */
506 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
507 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
508 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
510 /* Testing position way past end of text */
511 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
512 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
513 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
515 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
516 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
517 for (i = 0; i < 50; i++)
519 /* All the lines are 16 characters long */
520 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
521 ok((signed short)(HIWORD(result)) == (i - 1) * height,
522 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
523 (signed short)(HIWORD(result)), (i - 1) * height);
524 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
527 /* Testing position at end of text */
528 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
529 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
530 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
532 /* Testing position way past end of text */
533 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
534 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
535 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
537 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
538 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
539 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
541 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
542 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
543 todo_wine {
544 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
546 xpos = LOWORD(result);
548 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
549 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
550 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
551 todo_wine {
552 /* Fails on builtin because horizontal scrollbar is not being shown */
553 ok((signed short)(LOWORD(result)) < xpos,
554 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
555 (signed short)(LOWORD(result)), xpos);
557 DestroyWindow(hwndRichEdit);
560 static void test_EM_SETCHARFORMAT(void)
562 HWND hwndRichEdit = new_richedit(NULL);
563 CHARFORMAT2 cf2;
564 int rc = 0;
565 int tested_effects[] = {
566 CFE_BOLD,
567 CFE_ITALIC,
568 CFE_UNDERLINE,
569 CFE_STRIKEOUT,
570 CFE_PROTECTED,
571 CFE_LINK,
572 CFE_SUBSCRIPT,
573 CFE_SUPERSCRIPT,
576 int i;
577 CHARRANGE cr;
579 /* Invalid flags, CHARFORMAT2 structure blanked out */
580 memset(&cf2, 0, sizeof(cf2));
581 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
582 (LPARAM) &cf2);
583 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
585 /* A valid flag, CHARFORMAT2 structure blanked out */
586 memset(&cf2, 0, sizeof(cf2));
587 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
588 (LPARAM) &cf2);
589 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
591 /* A valid flag, CHARFORMAT2 structure blanked out */
592 memset(&cf2, 0, sizeof(cf2));
593 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
594 (LPARAM) &cf2);
595 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
597 /* A valid flag, CHARFORMAT2 structure blanked out */
598 memset(&cf2, 0, sizeof(cf2));
599 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
600 (LPARAM) &cf2);
601 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
603 /* A valid flag, CHARFORMAT2 structure blanked out */
604 memset(&cf2, 0, sizeof(cf2));
605 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
606 (LPARAM) &cf2);
607 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
609 /* Invalid flags, CHARFORMAT2 structure minimally filled */
610 memset(&cf2, 0, sizeof(cf2));
611 cf2.cbSize = sizeof(CHARFORMAT2);
612 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
613 (LPARAM) &cf2);
614 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
615 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
616 ok(rc == FALSE, "Should not be able to undo here.\n");
617 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
619 /* A valid flag, CHARFORMAT2 structure minimally filled */
620 memset(&cf2, 0, sizeof(cf2));
621 cf2.cbSize = sizeof(CHARFORMAT2);
622 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
623 (LPARAM) &cf2);
624 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
625 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
626 ok(rc == FALSE, "Should not be able to undo here.\n");
627 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
629 /* A valid flag, CHARFORMAT2 structure minimally filled */
630 memset(&cf2, 0, sizeof(cf2));
631 cf2.cbSize = sizeof(CHARFORMAT2);
632 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
633 (LPARAM) &cf2);
634 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
635 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
636 ok(rc == FALSE, "Should not be able to undo here.\n");
637 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
639 /* A valid flag, CHARFORMAT2 structure minimally filled */
640 memset(&cf2, 0, sizeof(cf2));
641 cf2.cbSize = sizeof(CHARFORMAT2);
642 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
643 (LPARAM) &cf2);
644 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
645 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
646 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
647 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
649 /* A valid flag, CHARFORMAT2 structure minimally filled */
650 memset(&cf2, 0, sizeof(cf2));
651 cf2.cbSize = sizeof(CHARFORMAT2);
652 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
653 (LPARAM) &cf2);
654 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
655 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
656 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
657 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
659 cf2.cbSize = sizeof(CHARFORMAT2);
660 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
661 (LPARAM) &cf2);
663 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
664 cf2.cbSize = sizeof(CHARFORMAT2);
665 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
666 (LPARAM) &cf2);
667 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
668 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
670 /* wParam==0 is default char format, does not set modify */
671 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
672 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
673 ok(rc == 0, "Text marked as modified, expected not modified!\n");
674 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
675 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
676 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
677 ok(rc == 0, "Text marked as modified, expected not modified!\n");
679 /* wParam==SCF_SELECTION sets modify if nonempty selection */
680 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
681 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
682 ok(rc == 0, "Text marked as modified, expected not modified!\n");
683 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
684 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
685 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
686 ok(rc == 0, "Text marked as modified, expected not modified!\n");
688 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
689 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
690 ok(rc == 0, "Text marked as modified, expected not modified!\n");
691 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
692 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
693 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
694 ok(rc == 0, "Text marked as modified, expected not modified!\n");
695 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
696 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
697 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
698 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
699 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
701 /* wParam==SCF_ALL sets modify regardless of whether text is present */
702 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
703 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
704 ok(rc == 0, "Text marked as modified, expected not modified!\n");
705 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
706 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
707 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
708 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
710 DestroyWindow(hwndRichEdit);
712 /* EM_GETCHARFORMAT tests */
713 for (i = 0; tested_effects[i]; i++)
715 hwndRichEdit = new_richedit(NULL);
716 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
718 /* Need to set a TrueType font to get consistent CFM_BOLD results */
719 memset(&cf2, 0, sizeof(CHARFORMAT2));
720 cf2.cbSize = sizeof(CHARFORMAT2);
721 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
722 cf2.dwEffects = 0;
723 strcpy(cf2.szFaceName, "Courier New");
724 cf2.wWeight = FW_DONTCARE;
725 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
727 memset(&cf2, 0, sizeof(CHARFORMAT2));
728 cf2.cbSize = sizeof(CHARFORMAT2);
729 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
730 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
731 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
732 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
734 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
735 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
736 ok((cf2.dwEffects & tested_effects[i]) == 0,
737 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
739 memset(&cf2, 0, sizeof(CHARFORMAT2));
740 cf2.cbSize = sizeof(CHARFORMAT2);
741 cf2.dwMask = tested_effects[i];
742 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
743 cf2.dwMask = CFM_SUPERSCRIPT;
744 cf2.dwEffects = tested_effects[i];
745 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
746 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
748 memset(&cf2, 0, sizeof(CHARFORMAT2));
749 cf2.cbSize = sizeof(CHARFORMAT2);
750 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
751 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
752 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
753 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
755 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
756 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
757 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
758 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
760 memset(&cf2, 0, sizeof(CHARFORMAT2));
761 cf2.cbSize = sizeof(CHARFORMAT2);
762 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
763 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
764 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
765 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
767 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
768 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
769 ok((cf2.dwEffects & tested_effects[i]) == 0,
770 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
772 memset(&cf2, 0, sizeof(CHARFORMAT2));
773 cf2.cbSize = sizeof(CHARFORMAT2);
774 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
775 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
776 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
777 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
779 (cf2.dwMask & tested_effects[i]) == 0),
780 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
782 DestroyWindow(hwndRichEdit);
785 for (i = 0; tested_effects[i]; i++)
787 hwndRichEdit = new_richedit(NULL);
788 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
790 /* Need to set a TrueType font to get consistent CFM_BOLD results */
791 memset(&cf2, 0, sizeof(CHARFORMAT2));
792 cf2.cbSize = sizeof(CHARFORMAT2);
793 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
794 cf2.dwEffects = 0;
795 strcpy(cf2.szFaceName, "Courier New");
796 cf2.wWeight = FW_DONTCARE;
797 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
799 memset(&cf2, 0, sizeof(CHARFORMAT2));
800 cf2.cbSize = sizeof(CHARFORMAT2);
801 cf2.dwMask = tested_effects[i];
802 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
803 cf2.dwMask = CFM_SUPERSCRIPT;
804 cf2.dwEffects = tested_effects[i];
805 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
806 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
808 memset(&cf2, 0, sizeof(CHARFORMAT2));
809 cf2.cbSize = sizeof(CHARFORMAT2);
810 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
811 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
812 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
813 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
815 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
816 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
817 ok((cf2.dwEffects & tested_effects[i]) == 0,
818 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
820 memset(&cf2, 0, sizeof(CHARFORMAT2));
821 cf2.cbSize = sizeof(CHARFORMAT2);
822 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
823 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
824 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
825 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
827 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
828 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
829 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
830 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
832 memset(&cf2, 0, sizeof(CHARFORMAT2));
833 cf2.cbSize = sizeof(CHARFORMAT2);
834 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
835 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
836 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
837 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
839 (cf2.dwMask & tested_effects[i]) == 0),
840 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
841 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
842 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
844 DestroyWindow(hwndRichEdit);
847 /* Effects applied on an empty selection should take effect when selection is
848 replaced with text */
849 hwndRichEdit = new_richedit(NULL);
850 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
851 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
853 memset(&cf2, 0, sizeof(CHARFORMAT2));
854 cf2.cbSize = sizeof(CHARFORMAT2);
855 cf2.dwMask = CFM_BOLD;
856 cf2.dwEffects = CFE_BOLD;
857 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
859 /* Selection is now nonempty */
860 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
862 memset(&cf2, 0, sizeof(CHARFORMAT2));
863 cf2.cbSize = sizeof(CHARFORMAT2);
864 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
865 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
867 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
868 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
869 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
870 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
873 /* Set two effects on an empty selection */
874 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
875 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
877 memset(&cf2, 0, sizeof(CHARFORMAT2));
878 cf2.cbSize = sizeof(CHARFORMAT2);
879 cf2.dwMask = CFM_BOLD;
880 cf2.dwEffects = CFE_BOLD;
881 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
882 cf2.dwMask = CFM_ITALIC;
883 cf2.dwEffects = CFE_ITALIC;
884 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
886 /* Selection is now nonempty */
887 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
889 memset(&cf2, 0, sizeof(CHARFORMAT2));
890 cf2.cbSize = sizeof(CHARFORMAT2);
891 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
892 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
894 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
895 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
896 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
897 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
899 /* Setting the (empty) selection to exactly the same place as before should
900 NOT clear the insertion style! */
901 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
902 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
904 memset(&cf2, 0, sizeof(CHARFORMAT2));
905 cf2.cbSize = sizeof(CHARFORMAT2);
906 cf2.dwMask = CFM_BOLD;
907 cf2.dwEffects = CFE_BOLD;
908 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
910 /* Empty selection in same place, insert style should NOT be forgotten here. */
911 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
913 /* Selection is now nonempty */
914 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
916 memset(&cf2, 0, sizeof(CHARFORMAT2));
917 cf2.cbSize = sizeof(CHARFORMAT2);
918 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
919 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
921 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
922 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
923 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
924 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
926 /* Ditto with EM_EXSETSEL */
927 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
928 cr.cpMin = 2; cr.cpMax = 2;
929 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
931 memset(&cf2, 0, sizeof(CHARFORMAT2));
932 cf2.cbSize = sizeof(CHARFORMAT2);
933 cf2.dwMask = CFM_BOLD;
934 cf2.dwEffects = CFE_BOLD;
935 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
937 /* Empty selection in same place, insert style should NOT be forgotten here. */
938 cr.cpMin = 2; cr.cpMax = 2;
939 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
941 /* Selection is now nonempty */
942 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
944 memset(&cf2, 0, sizeof(CHARFORMAT2));
945 cf2.cbSize = sizeof(CHARFORMAT2);
946 cr.cpMin = 2; cr.cpMax = 6;
947 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
948 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
950 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
951 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
952 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
953 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
955 DestroyWindow(hwndRichEdit);
958 static void test_EM_SETTEXTMODE(void)
960 HWND hwndRichEdit = new_richedit(NULL);
961 CHARFORMAT2 cf2, cf2test;
962 CHARRANGE cr;
963 int rc = 0;
965 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
966 /*Insert text into the control*/
968 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
970 /*Attempt to change the control to plain text mode*/
971 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
972 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
974 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
975 If rich text is pasted, it should have the same formatting as the rest
976 of the text in the control*/
978 /*Italicize the text
979 *NOTE: If the default text was already italicized, the test will simply
980 reverse; in other words, it will copy a regular "wine" into a plain
981 text window that uses an italicized format*/
982 cf2.cbSize = sizeof(CHARFORMAT2);
983 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
984 (LPARAM) &cf2);
986 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
987 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
989 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
990 ok(rc == 0, "Text marked as modified, expected not modified!\n");
992 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
993 however, SCF_ALL has been implemented*/
994 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
995 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
997 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
998 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1000 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1002 /*Select the string "wine"*/
1003 cr.cpMin = 0;
1004 cr.cpMax = 4;
1005 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1007 /*Copy the italicized "wine" to the clipboard*/
1008 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1010 /*Reset the formatting to default*/
1011 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1012 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1013 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1015 /*Clear the text in the control*/
1016 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1018 /*Switch to Plain Text Mode*/
1019 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1020 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1022 /*Input "wine" again in normal format*/
1023 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1025 /*Paste the italicized "wine" into the control*/
1026 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1028 /*Select a character from the first "wine" string*/
1029 cr.cpMin = 2;
1030 cr.cpMax = 3;
1031 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1033 /*Retrieve its formatting*/
1034 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1035 (LPARAM) &cf2);
1037 /*Select a character from the second "wine" string*/
1038 cr.cpMin = 5;
1039 cr.cpMax = 6;
1040 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1042 /*Retrieve its formatting*/
1043 cf2test.cbSize = sizeof(CHARFORMAT2);
1044 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1045 (LPARAM) &cf2test);
1047 /*Compare the two formattings*/
1048 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1049 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1050 cf2.dwEffects, cf2test.dwEffects);
1051 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1052 printing "wine" in the current format(normal)
1053 pasting "wine" from the clipboard(italicized)
1054 comparing the two formats(should differ)*/
1056 /*Attempt to switch with text in control*/
1057 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1058 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1060 /*Clear control*/
1061 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1063 /*Switch into Rich Text mode*/
1064 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1065 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1067 /*Print "wine" in normal formatting into the control*/
1068 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1070 /*Paste italicized "wine" into the control*/
1071 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1073 /*Select text from the first "wine" string*/
1074 cr.cpMin = 1;
1075 cr.cpMax = 3;
1076 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1078 /*Retrieve its formatting*/
1079 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1080 (LPARAM) &cf2);
1082 /*Select text from the second "wine" string*/
1083 cr.cpMin = 6;
1084 cr.cpMax = 7;
1085 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1087 /*Retrieve its formatting*/
1088 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1089 (LPARAM) &cf2test);
1091 /*Test that the two formattings are not the same*/
1092 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1093 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1094 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1096 DestroyWindow(hwndRichEdit);
1099 static void test_SETPARAFORMAT(void)
1101 HWND hwndRichEdit = new_richedit(NULL);
1102 PARAFORMAT2 fmt;
1103 HRESULT ret;
1104 fmt.cbSize = sizeof(PARAFORMAT2);
1105 fmt.dwMask = PFM_ALIGNMENT;
1106 fmt.wAlignment = PFA_LEFT;
1108 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1109 ok(ret != 0, "expected non-zero got %d\n", ret);
1111 fmt.cbSize = sizeof(PARAFORMAT2);
1112 fmt.dwMask = -1;
1113 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1114 ok(ret == PFM_ALL2, "expected %x got %x\n", PFM_ALL2, ret);
1115 ok(fmt.dwMask == PFM_ALL2, "expected %x got %x\n", PFM_ALL2, fmt.dwMask);
1117 DestroyWindow(hwndRichEdit);
1120 static void test_TM_PLAINTEXT(void)
1122 /*Tests plain text properties*/
1124 HWND hwndRichEdit = new_richedit(NULL);
1125 CHARFORMAT2 cf2, cf2test;
1126 CHARRANGE cr;
1127 int rc = 0;
1129 /*Switch to plain text mode*/
1131 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1132 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1134 /*Fill control with text*/
1136 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1138 /*Select some text and bold it*/
1140 cr.cpMin = 10;
1141 cr.cpMax = 20;
1142 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1143 cf2.cbSize = sizeof(CHARFORMAT2);
1144 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1145 (LPARAM) &cf2);
1147 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1148 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1150 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1151 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1153 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1154 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1156 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1157 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1159 /*Get the formatting of those characters*/
1161 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1163 /*Get the formatting of some other characters*/
1164 cf2test.cbSize = sizeof(CHARFORMAT2);
1165 cr.cpMin = 21;
1166 cr.cpMax = 30;
1167 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1168 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1170 /*Test that they are the same as plain text allows only one formatting*/
1172 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1173 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1174 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1176 /*Fill the control with a "wine" string, which when inserted will be bold*/
1178 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1180 /*Copy the bolded "wine" string*/
1182 cr.cpMin = 0;
1183 cr.cpMax = 4;
1184 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1185 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1187 /*Swap back to rich text*/
1189 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1190 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1192 /*Set the default formatting to bold italics*/
1194 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1195 cf2.dwMask |= CFM_ITALIC;
1196 cf2.dwEffects ^= CFE_ITALIC;
1197 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1198 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1200 /*Set the text in the control to "wine", which will be bold and italicized*/
1202 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1204 /*Paste the plain text "wine" string, which should take the insert
1205 formatting, which at the moment is bold italics*/
1207 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1209 /*Select the first "wine" string and retrieve its formatting*/
1211 cr.cpMin = 1;
1212 cr.cpMax = 3;
1213 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1214 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1216 /*Select the second "wine" string and retrieve its formatting*/
1218 cr.cpMin = 5;
1219 cr.cpMax = 7;
1220 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1221 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1223 /*Compare the two formattings. They should be the same.*/
1225 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1226 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1227 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1228 DestroyWindow(hwndRichEdit);
1231 static void test_WM_GETTEXT(void)
1233 HWND hwndRichEdit = new_richedit(NULL);
1234 static const char text[] = "Hello. My name is RichEdit!";
1235 static const char text2[] = "Hello. My name is RichEdit!\r";
1236 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1237 char buffer[1024] = {0};
1238 int result;
1240 /* Baseline test with normal-sized buffer */
1241 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1242 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1243 ok(result == lstrlen(buffer),
1244 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1245 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1246 result = strcmp(buffer,text);
1247 ok(result == 0,
1248 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1250 /* Test for returned value of WM_GETTEXTLENGTH */
1251 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1252 ok(result == lstrlen(text),
1253 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1254 result, lstrlen(text));
1256 /* Test for behavior in overflow case */
1257 memset(buffer, 0, 1024);
1258 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1259 ok(result == 0 ||
1260 result == lstrlenA(text) - 1, /* XP, win2k3 */
1261 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1262 result = strcmp(buffer,text);
1263 if (result)
1264 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1265 ok(result == 0,
1266 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1268 /* Baseline test with normal-sized buffer and carriage return */
1269 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1270 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1271 ok(result == lstrlen(buffer),
1272 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1273 result = strcmp(buffer,text2_after);
1274 ok(result == 0,
1275 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1277 /* Test for returned value of WM_GETTEXTLENGTH */
1278 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1279 ok(result == lstrlen(text2_after),
1280 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1281 result, lstrlen(text2_after));
1283 /* Test for behavior of CRLF conversion in case of overflow */
1284 memset(buffer, 0, 1024);
1285 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1286 ok(result == 0 ||
1287 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1288 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1289 result = strcmp(buffer,text2);
1290 if (result)
1291 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1292 ok(result == 0,
1293 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1295 DestroyWindow(hwndRichEdit);
1298 static void test_EM_GETTEXTRANGE(void)
1300 HWND hwndRichEdit = new_richedit(NULL);
1301 const char * text1 = "foo bar\r\nfoo bar";
1302 const char * text2 = "foo bar\rfoo bar";
1303 const char * expect = "bar\rfoo";
1304 char buffer[1024] = {0};
1305 LRESULT result;
1306 TEXTRANGEA textRange;
1308 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1310 textRange.lpstrText = buffer;
1311 textRange.chrg.cpMin = 4;
1312 textRange.chrg.cpMax = 11;
1313 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1314 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1315 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1319 textRange.lpstrText = buffer;
1320 textRange.chrg.cpMin = 4;
1321 textRange.chrg.cpMax = 11;
1322 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1323 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1324 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1326 DestroyWindow(hwndRichEdit);
1329 static void test_EM_GETSELTEXT(void)
1331 HWND hwndRichEdit = new_richedit(NULL);
1332 const char * text1 = "foo bar\r\nfoo bar";
1333 const char * text2 = "foo bar\rfoo bar";
1334 const char * expect = "bar\rfoo";
1335 char buffer[1024] = {0};
1336 LRESULT result;
1338 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1340 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1341 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1342 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1343 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1345 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1347 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1348 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1349 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1350 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1352 DestroyWindow(hwndRichEdit);
1355 /* FIXME: need to test unimplemented options and robustly test wparam */
1356 static void test_EM_SETOPTIONS(void)
1358 HWND hwndRichEdit = new_richedit(NULL);
1359 static const char text[] = "Hello. My name is RichEdit!";
1360 char buffer[1024] = {0};
1362 /* NEGATIVE TESTING - NO OPTIONS SET */
1363 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1364 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1366 /* testing no readonly by sending 'a' to the control*/
1367 SetFocus(hwndRichEdit);
1368 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1369 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1370 ok(buffer[0]=='a',
1371 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1372 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1374 /* READONLY - sending 'a' to the control */
1375 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1376 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1377 SetFocus(hwndRichEdit);
1378 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1379 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1380 ok(buffer[0]==text[0],
1381 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1383 DestroyWindow(hwndRichEdit);
1386 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1388 CHARFORMAT2W text_format;
1389 text_format.cbSize = sizeof(text_format);
1390 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1391 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1392 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1395 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1397 int link_present = 0;
1399 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1400 if (is_url)
1401 { /* control text is url; should get CFE_LINK */
1402 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1404 else
1406 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1410 static HWND new_static_wnd(HWND parent) {
1411 return new_window("Static", 0, parent);
1414 static void test_EM_AUTOURLDETECT(void)
1416 /* DO NOT change the properties of the first two elements. To shorten the
1417 tests, all tests after WM_SETTEXT test just the first two elements -
1418 one non-URL and one URL */
1419 struct urls_s {
1420 const char *text;
1421 int is_url;
1422 } urls[12] = {
1423 {"winehq.org", 0},
1424 {"http://www.winehq.org", 1},
1425 {"http//winehq.org", 0},
1426 {"ww.winehq.org", 0},
1427 {"www.winehq.org", 1},
1428 {"ftp://192.168.1.1", 1},
1429 {"ftp//192.168.1.1", 0},
1430 {"mailto:your@email.com", 1},
1431 {"prospero:prosperoserver", 1},
1432 {"telnet:test", 1},
1433 {"news:newserver", 1},
1434 {"wais:waisserver", 1}
1437 int i, j;
1438 int urlRet=-1;
1439 HWND hwndRichEdit, parent;
1441 /* All of the following should cause the URL to be detected */
1442 const char * templates_delim[] = {
1443 "This is some text with X on it",
1444 "This is some text with (X) on it",
1445 "This is some text with X\r on it",
1446 "This is some text with ---X--- on it",
1447 "This is some text with \"X\" on it",
1448 "This is some text with 'X' on it",
1449 "This is some text with 'X' on it",
1450 "This is some text with :X: on it",
1452 "This text ends with X",
1454 "This is some text with X) on it",
1455 "This is some text with X--- on it",
1456 "This is some text with X\" on it",
1457 "This is some text with X' on it",
1458 "This is some text with X: on it",
1460 "This is some text with (X on it",
1461 "This is some text with \rX on it",
1462 "This is some text with ---X on it",
1463 "This is some text with \"X on it",
1464 "This is some text with 'X on it",
1465 "This is some text with :X on it",
1467 /* None of these should cause the URL to be detected */
1468 const char * templates_non_delim[] = {
1469 "This is some text with |X| on it",
1470 "This is some text with *X* on it",
1471 "This is some text with /X/ on it",
1472 "This is some text with +X+ on it",
1473 "This is some text with %X% on it",
1474 "This is some text with #X# on it",
1475 "This is some text with @X@ on it",
1476 "This is some text with \\X\\ on it",
1477 "This is some text with |X on it",
1478 "This is some text with *X on it",
1479 "This is some text with /X on it",
1480 "This is some text with +X on it",
1481 "This is some text with %X on it",
1482 "This is some text with #X on it",
1483 "This is some text with @X on it",
1484 "This is some text with \\X on it",
1486 /* All of these cause the URL detection to be extended by one more byte,
1487 thus demonstrating that the tested character is considered as part
1488 of the URL. */
1489 const char * templates_xten_delim[] = {
1490 "This is some text with X| on it",
1491 "This is some text with X* on it",
1492 "This is some text with X/ on it",
1493 "This is some text with X+ on it",
1494 "This is some text with X% on it",
1495 "This is some text with X# on it",
1496 "This is some text with X@ on it",
1497 "This is some text with X\\ on it",
1499 char buffer[1024];
1501 parent = new_static_wnd(NULL);
1502 hwndRichEdit = new_richedit(parent);
1503 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1504 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1505 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1506 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1507 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1508 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1509 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1510 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1511 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1512 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1513 /* for each url, check the text to see if CFE_LINK effect is present */
1514 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1516 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1517 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1518 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1520 /* Link detection should happen immediately upon WM_SETTEXT */
1521 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1522 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1523 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1525 DestroyWindow(hwndRichEdit);
1527 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1528 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1529 hwndRichEdit = new_richedit(parent);
1531 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1532 char * at_pos;
1533 int at_offset;
1534 int end_offset;
1536 at_pos = strchr(templates_delim[j], 'X');
1537 at_offset = at_pos - templates_delim[j];
1538 strncpy(buffer, templates_delim[j], at_offset);
1539 buffer[at_offset] = '\0';
1540 strcat(buffer, urls[i].text);
1541 strcat(buffer, templates_delim[j] + at_offset + 1);
1542 end_offset = at_offset + strlen(urls[i].text);
1544 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1545 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1547 /* This assumes no templates start with the URL itself, and that they
1548 have at least two characters before the URL text */
1549 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1550 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1551 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1552 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1553 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1554 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1556 if (urls[i].is_url)
1558 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1559 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1560 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1561 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1563 else
1565 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1566 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1567 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1568 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1570 if (buffer[end_offset] != '\0')
1572 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1573 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1574 if (buffer[end_offset +1] != '\0')
1576 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1577 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1582 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1583 char * at_pos;
1584 int at_offset;
1585 int end_offset;
1587 at_pos = strchr(templates_non_delim[j], 'X');
1588 at_offset = at_pos - templates_non_delim[j];
1589 strncpy(buffer, templates_non_delim[j], at_offset);
1590 buffer[at_offset] = '\0';
1591 strcat(buffer, urls[i].text);
1592 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1593 end_offset = at_offset + strlen(urls[i].text);
1595 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1596 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1598 /* This assumes no templates start with the URL itself, and that they
1599 have at least two characters before the URL text */
1600 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1601 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1602 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1603 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1604 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1605 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1607 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1608 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1609 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1610 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1611 if (buffer[end_offset] != '\0')
1613 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1614 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1615 if (buffer[end_offset +1] != '\0')
1617 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1618 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1623 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1624 char * at_pos;
1625 int at_offset;
1626 int end_offset;
1628 at_pos = strchr(templates_xten_delim[j], 'X');
1629 at_offset = at_pos - templates_xten_delim[j];
1630 strncpy(buffer, templates_xten_delim[j], at_offset);
1631 buffer[at_offset] = '\0';
1632 strcat(buffer, urls[i].text);
1633 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1634 end_offset = at_offset + strlen(urls[i].text);
1636 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1637 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1639 /* This assumes no templates start with the URL itself, and that they
1640 have at least two characters before the URL text */
1641 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1642 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1643 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1644 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1645 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1646 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1648 if (urls[i].is_url)
1650 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1651 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1652 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1653 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1654 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1655 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1657 else
1659 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1660 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1661 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1662 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1663 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1664 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1666 if (buffer[end_offset +1] != '\0')
1668 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1669 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1670 if (buffer[end_offset +2] != '\0')
1672 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1673 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1678 DestroyWindow(hwndRichEdit);
1679 hwndRichEdit = NULL;
1682 /* Test detection of URLs within normal text - WM_CHAR case. */
1683 /* Test only the first two URL examples for brevity */
1684 for (i = 0; i < 2; i++) {
1685 hwndRichEdit = new_richedit(parent);
1687 /* Also for brevity, test only the first three delimiters */
1688 for (j = 0; j < 3; j++) {
1689 char * at_pos;
1690 int at_offset;
1691 int end_offset;
1692 int u, v;
1694 at_pos = strchr(templates_delim[j], 'X');
1695 at_offset = at_pos - templates_delim[j];
1696 end_offset = at_offset + strlen(urls[i].text);
1698 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1699 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1700 for (u = 0; templates_delim[j][u]; u++) {
1701 if (templates_delim[j][u] == '\r') {
1702 simulate_typing_characters(hwndRichEdit, "\r");
1703 } else if (templates_delim[j][u] != 'X') {
1704 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1705 } else {
1706 for (v = 0; urls[i].text[v]; v++) {
1707 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1711 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1713 /* This assumes no templates start with the URL itself, and that they
1714 have at least two characters before the URL text */
1715 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1716 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1717 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1718 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1719 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1720 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1722 if (urls[i].is_url)
1724 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1725 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1726 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1727 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1729 else
1731 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1732 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1733 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1734 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1736 if (buffer[end_offset] != '\0')
1738 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1739 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1740 if (buffer[end_offset +1] != '\0')
1742 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1743 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1747 /* The following will insert a paragraph break after the first character
1748 of the URL candidate, thus breaking the URL. It is expected that the
1749 CFE_LINK attribute should break across both pieces of the URL */
1750 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1751 simulate_typing_characters(hwndRichEdit, "\r");
1752 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1754 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1755 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1756 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1757 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1758 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1759 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1761 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1762 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1763 /* end_offset moved because of paragraph break */
1764 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1765 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1766 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1767 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1769 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1770 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1771 if (buffer[end_offset +2] != '\0')
1773 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1774 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1778 /* The following will remove the just-inserted paragraph break, thus
1779 restoring the URL */
1780 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1781 simulate_typing_characters(hwndRichEdit, "\b");
1782 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1784 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1785 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1786 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1787 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1788 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1789 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1791 if (urls[i].is_url)
1793 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1794 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1795 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1796 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1798 else
1800 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1801 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1802 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1803 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1805 if (buffer[end_offset] != '\0')
1807 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1808 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1809 if (buffer[end_offset +1] != '\0')
1811 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1812 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1816 DestroyWindow(hwndRichEdit);
1817 hwndRichEdit = NULL;
1820 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1821 /* Test just the first two URL examples for brevity */
1822 for (i = 0; i < 2; i++) {
1823 SETTEXTEX st;
1825 hwndRichEdit = new_richedit(parent);
1827 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1828 be detected:
1829 1) Set entire text, a la WM_SETTEXT
1830 2) Set a selection of the text to the URL
1831 3) Set a portion of the text at a time, which eventually results in
1832 an URL
1833 All of them should give equivalent results
1836 /* Set entire text in one go, like WM_SETTEXT */
1837 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1838 char * at_pos;
1839 int at_offset;
1840 int end_offset;
1842 st.codepage = CP_ACP;
1843 st.flags = ST_DEFAULT;
1845 at_pos = strchr(templates_delim[j], 'X');
1846 at_offset = at_pos - templates_delim[j];
1847 strncpy(buffer, templates_delim[j], at_offset);
1848 buffer[at_offset] = '\0';
1849 strcat(buffer, urls[i].text);
1850 strcat(buffer, templates_delim[j] + at_offset + 1);
1851 end_offset = at_offset + strlen(urls[i].text);
1853 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1854 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (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);
1872 else
1874 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1875 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1876 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1877 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1879 if (buffer[end_offset] != '\0')
1881 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1882 "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);
1891 /* Set selection with X to the URL */
1892 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1893 char * at_pos;
1894 int at_offset;
1895 int end_offset;
1897 at_pos = strchr(templates_delim[j], 'X');
1898 at_offset = at_pos - templates_delim[j];
1899 end_offset = at_offset + strlen(urls[i].text);
1901 st.codepage = CP_ACP;
1902 st.flags = ST_DEFAULT;
1903 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1904 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1905 st.flags = ST_SELECTION;
1906 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1907 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1908 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1910 /* This assumes no templates start with the URL itself, and that they
1911 have at least two characters before the URL text */
1912 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1913 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1914 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1915 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1916 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1917 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1919 if (urls[i].is_url)
1921 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1922 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1923 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1924 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1926 else
1928 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1929 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1930 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1931 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1933 if (buffer[end_offset] != '\0')
1935 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1936 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1937 if (buffer[end_offset +1] != '\0')
1939 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1940 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1945 /* Set selection with X to the first character of the URL, then the rest */
1946 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1947 char * at_pos;
1948 int at_offset;
1949 int end_offset;
1951 at_pos = strchr(templates_delim[j], 'X');
1952 at_offset = at_pos - templates_delim[j];
1953 end_offset = at_offset + strlen(urls[i].text);
1955 strcpy(buffer, "YY");
1956 buffer[0] = urls[i].text[0];
1958 st.codepage = CP_ACP;
1959 st.flags = ST_DEFAULT;
1960 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1961 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1962 st.flags = ST_SELECTION;
1963 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1964 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1965 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1966 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1967 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1969 /* This assumes no templates start with the URL itself, and that they
1970 have at least two characters before the URL text */
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 if (urls[i].is_url)
1980 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1981 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1982 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1983 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1985 else
1987 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1988 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1989 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1990 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1992 if (buffer[end_offset] != '\0')
1994 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1995 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1996 if (buffer[end_offset +1] != '\0')
1998 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1999 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2004 DestroyWindow(hwndRichEdit);
2005 hwndRichEdit = NULL;
2008 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2009 /* Test just the first two URL examples for brevity */
2010 for (i = 0; i < 2; i++) {
2011 hwndRichEdit = new_richedit(parent);
2013 /* Set selection with X to the URL */
2014 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2015 char * at_pos;
2016 int at_offset;
2017 int end_offset;
2019 at_pos = strchr(templates_delim[j], 'X');
2020 at_offset = at_pos - templates_delim[j];
2021 end_offset = at_offset + strlen(urls[i].text);
2023 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2024 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2025 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2026 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2027 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2029 /* This assumes no templates start with the URL itself, and that they
2030 have at least two characters before the URL text */
2031 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2032 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2033 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2034 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2035 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2036 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2038 if (urls[i].is_url)
2040 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2041 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2042 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2043 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2045 else
2047 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2048 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2049 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2050 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2052 if (buffer[end_offset] != '\0')
2054 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2055 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2056 if (buffer[end_offset +1] != '\0')
2058 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2059 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2064 /* Set selection with X to the first character of the URL, then the rest */
2065 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2066 char * at_pos;
2067 int at_offset;
2068 int end_offset;
2070 at_pos = strchr(templates_delim[j], 'X');
2071 at_offset = at_pos - templates_delim[j];
2072 end_offset = at_offset + strlen(urls[i].text);
2074 strcpy(buffer, "YY");
2075 buffer[0] = urls[i].text[0];
2077 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2078 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2079 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2080 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2081 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2082 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2083 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2085 /* This assumes no templates start with the URL itself, and that they
2086 have at least two characters before the URL text */
2087 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2088 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2089 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2090 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2091 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2092 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2094 if (urls[i].is_url)
2096 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2097 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2098 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2099 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2101 else
2103 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2104 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2105 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2106 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2108 if (buffer[end_offset] != '\0')
2110 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2111 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2112 if (buffer[end_offset +1] != '\0')
2114 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2115 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2120 DestroyWindow(hwndRichEdit);
2121 hwndRichEdit = NULL;
2124 DestroyWindow(parent);
2127 static void test_EM_SCROLL(void)
2129 int i, j;
2130 int r; /* return value */
2131 int expr; /* expected return value */
2132 HWND hwndRichEdit = new_richedit(NULL);
2133 int y_before, y_after; /* units of lines of text */
2135 /* test a richedit box containing a single line of text */
2136 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2137 expr = 0x00010000;
2138 for (i = 0; i < 4; i++) {
2139 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2141 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2142 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2143 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2144 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2145 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2146 "(i == %d)\n", y_after, i);
2150 * test a richedit box that will scroll. There are two general
2151 * cases: the case without any long lines and the case with a long
2152 * line.
2154 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2155 if (i == 0)
2156 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2157 else
2158 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2159 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2160 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2161 "LONG LINE \nb\nc\nd\ne");
2162 for (j = 0; j < 12; j++) /* reset scroll position to top */
2163 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2165 /* get first visible line */
2166 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2167 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2169 /* get new current first visible line */
2170 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2172 ok(((r & 0xffffff00) == 0x00010000) &&
2173 ((r & 0x000000ff) != 0x00000000),
2174 "EM_SCROLL page down didn't scroll by a small positive number of "
2175 "lines (r == 0x%08x)\n", r);
2176 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2177 "(line %d scrolled to line %d\n", y_before, y_after);
2179 y_before = y_after;
2181 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2182 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2183 ok(((r & 0xffffff00) == 0x0001ff00),
2184 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2185 "(r == 0x%08x)\n", r);
2186 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2187 "%d scrolled to line %d\n", y_before, y_after);
2189 y_before = y_after;
2191 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2193 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2195 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2196 "(r == 0x%08x)\n", r);
2197 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2198 "1 line (%d scrolled to %d)\n", y_before, y_after);
2200 y_before = y_after;
2202 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2204 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2206 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2207 "(r == 0x%08x)\n", r);
2208 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2209 "line (%d scrolled to %d)\n", y_before, y_after);
2211 y_before = y_after;
2213 r = SendMessage(hwndRichEdit, EM_SCROLL,
2214 SB_LINEUP, 0); /* lineup beyond top */
2216 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2218 ok(r == 0x00010000,
2219 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2220 ok(y_before == y_after,
2221 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2223 y_before = y_after;
2225 r = SendMessage(hwndRichEdit, EM_SCROLL,
2226 SB_PAGEUP, 0);/*page up beyond top */
2228 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2230 ok(r == 0x00010000,
2231 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2232 ok(y_before == y_after,
2233 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2235 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2236 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2237 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2238 r = SendMessage(hwndRichEdit, EM_SCROLL,
2239 SB_PAGEDOWN, 0); /* page down beyond bot */
2240 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2242 ok(r == 0x00010000,
2243 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2244 ok(y_before == y_after,
2245 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2246 y_before, y_after);
2248 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2249 SendMessage(hwndRichEdit, EM_SCROLL,
2250 SB_LINEDOWN, 0); /* line down beyond bot */
2251 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2253 ok(r == 0x00010000,
2254 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2255 ok(y_before == y_after,
2256 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2257 y_before, y_after);
2259 DestroyWindow(hwndRichEdit);
2262 unsigned int recursionLevel = 0;
2263 unsigned int WM_SIZE_recursionLevel = 0;
2264 BOOL bailedOutOfRecursion = FALSE;
2265 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2267 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2269 LRESULT r;
2271 if (bailedOutOfRecursion) return 0;
2272 if (recursionLevel >= 32) {
2273 bailedOutOfRecursion = TRUE;
2274 return 0;
2277 recursionLevel++;
2278 switch (message) {
2279 case WM_SIZE:
2280 WM_SIZE_recursionLevel++;
2281 r = richeditProc(hwnd, message, wParam, lParam);
2282 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2283 ShowScrollBar(hwnd, SB_VERT, TRUE);
2284 WM_SIZE_recursionLevel--;
2285 break;
2286 default:
2287 r = richeditProc(hwnd, message, wParam, lParam);
2288 break;
2290 recursionLevel--;
2291 return r;
2294 static void test_scrollbar_visibility(void)
2296 HWND hwndRichEdit;
2297 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2298 SCROLLINFO si;
2299 WNDCLASSA cls;
2300 BOOL r;
2302 /* These tests show that richedit should temporarily refrain from automatically
2303 hiding or showing its scrollbars (vertical at least) when an explicit request
2304 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2305 Some applications depend on forced showing (when otherwise richedit would
2306 hide the vertical scrollbar) and are thrown on an endless recursive loop
2307 if richedit auto-hides the scrollbar again. Apparently they never heard of
2308 the ES_DISABLENOSCROLL style... */
2310 hwndRichEdit = new_richedit(NULL);
2312 /* Test default scrollbar visibility behavior */
2313 memset(&si, 0, sizeof(si));
2314 si.cbSize = sizeof(si);
2315 si.fMask = SIF_PAGE | SIF_RANGE;
2316 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2317 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2318 "Vertical scrollbar is visible, should be invisible.\n");
2319 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2320 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2321 si.nPage, si.nMin, si.nMax);
2323 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2324 memset(&si, 0, sizeof(si));
2325 si.cbSize = sizeof(si);
2326 si.fMask = SIF_PAGE | SIF_RANGE;
2327 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2328 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2329 "Vertical scrollbar is visible, should be invisible.\n");
2330 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2331 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2332 si.nPage, si.nMin, si.nMax);
2334 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2335 memset(&si, 0, sizeof(si));
2336 si.cbSize = sizeof(si);
2337 si.fMask = SIF_PAGE | SIF_RANGE;
2338 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2339 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2340 "Vertical scrollbar is invisible, should be visible.\n");
2341 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2342 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2343 si.nPage, si.nMin, si.nMax);
2345 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2346 even though it hides the scrollbar */
2347 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2348 memset(&si, 0, sizeof(si));
2349 si.cbSize = sizeof(si);
2350 si.fMask = SIF_PAGE | SIF_RANGE;
2351 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2352 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2353 "Vertical scrollbar is visible, should be invisible.\n");
2354 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2355 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2356 si.nPage, si.nMin, si.nMax);
2358 /* Setting non-scrolling text again does *not* reset scrollbar range */
2359 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2360 memset(&si, 0, sizeof(si));
2361 si.cbSize = sizeof(si);
2362 si.fMask = SIF_PAGE | SIF_RANGE;
2363 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2364 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2365 "Vertical scrollbar is visible, should be invisible.\n");
2366 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2367 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2368 si.nPage, si.nMin, si.nMax);
2370 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2371 memset(&si, 0, sizeof(si));
2372 si.cbSize = sizeof(si);
2373 si.fMask = SIF_PAGE | SIF_RANGE;
2374 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2375 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2376 "Vertical scrollbar is visible, should be invisible.\n");
2377 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2378 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2379 si.nPage, si.nMin, si.nMax);
2381 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2382 memset(&si, 0, sizeof(si));
2383 si.cbSize = sizeof(si);
2384 si.fMask = SIF_PAGE | SIF_RANGE;
2385 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2386 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2387 "Vertical scrollbar is visible, should be invisible.\n");
2388 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2389 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2390 si.nPage, si.nMin, si.nMax);
2392 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2393 memset(&si, 0, sizeof(si));
2394 si.cbSize = sizeof(si);
2395 si.fMask = SIF_PAGE | SIF_RANGE;
2396 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2397 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2398 "Vertical scrollbar is visible, should be invisible.\n");
2399 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2400 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2401 si.nPage, si.nMin, si.nMax);
2403 DestroyWindow(hwndRichEdit);
2405 /* Test again, with ES_DISABLENOSCROLL style */
2406 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2408 /* Test default scrollbar visibility behavior */
2409 memset(&si, 0, sizeof(si));
2410 si.cbSize = sizeof(si);
2411 si.fMask = SIF_PAGE | SIF_RANGE;
2412 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2413 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2414 "Vertical scrollbar is invisible, should be visible.\n");
2415 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2416 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2417 si.nPage, si.nMin, si.nMax);
2419 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2420 memset(&si, 0, sizeof(si));
2421 si.cbSize = sizeof(si);
2422 si.fMask = SIF_PAGE | SIF_RANGE;
2423 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2424 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2425 "Vertical scrollbar is invisible, should be visible.\n");
2426 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2427 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2428 si.nPage, si.nMin, si.nMax);
2430 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2431 memset(&si, 0, sizeof(si));
2432 si.cbSize = sizeof(si);
2433 si.fMask = SIF_PAGE | SIF_RANGE;
2434 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2435 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2436 "Vertical scrollbar is invisible, should be visible.\n");
2437 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2438 "reported page/range is %d (%d..%d)\n",
2439 si.nPage, si.nMin, si.nMax);
2441 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2442 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2443 memset(&si, 0, sizeof(si));
2444 si.cbSize = sizeof(si);
2445 si.fMask = SIF_PAGE | SIF_RANGE;
2446 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2447 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2448 "Vertical scrollbar is invisible, should be visible.\n");
2449 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2450 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2451 si.nPage, si.nMin, si.nMax);
2453 /* Setting non-scrolling text again does *not* reset scrollbar range */
2454 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2455 memset(&si, 0, sizeof(si));
2456 si.cbSize = sizeof(si);
2457 si.fMask = SIF_PAGE | SIF_RANGE;
2458 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2459 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2460 "Vertical scrollbar is invisible, should be visible.\n");
2461 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2462 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2463 si.nPage, si.nMin, si.nMax);
2465 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2466 memset(&si, 0, sizeof(si));
2467 si.cbSize = sizeof(si);
2468 si.fMask = SIF_PAGE | SIF_RANGE;
2469 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2470 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2471 "Vertical scrollbar is invisible, should be visible.\n");
2472 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2473 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2474 si.nPage, si.nMin, si.nMax);
2476 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2477 memset(&si, 0, sizeof(si));
2478 si.cbSize = sizeof(si);
2479 si.fMask = SIF_PAGE | SIF_RANGE;
2480 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2481 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2482 "Vertical scrollbar is invisible, should be visible.\n");
2483 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2484 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2485 si.nPage, si.nMin, si.nMax);
2487 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2488 memset(&si, 0, sizeof(si));
2489 si.cbSize = sizeof(si);
2490 si.fMask = SIF_PAGE | SIF_RANGE;
2491 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2492 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2493 "Vertical scrollbar is invisible, should be visible.\n");
2494 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2495 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2496 si.nPage, si.nMin, si.nMax);
2498 DestroyWindow(hwndRichEdit);
2500 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2501 hwndRichEdit = new_richedit(NULL);
2503 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2504 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2505 memset(&si, 0, sizeof(si));
2506 si.cbSize = sizeof(si);
2507 si.fMask = SIF_PAGE | SIF_RANGE;
2508 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2509 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2510 "Vertical scrollbar is invisible, should be visible.\n");
2511 todo_wine {
2512 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2513 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2514 si.nPage, si.nMin, si.nMax);
2517 /* Ditto, see above */
2518 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2519 memset(&si, 0, sizeof(si));
2520 si.cbSize = sizeof(si);
2521 si.fMask = SIF_PAGE | SIF_RANGE;
2522 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2523 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2524 "Vertical scrollbar is invisible, should be visible.\n");
2525 todo_wine {
2526 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2527 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2528 si.nPage, si.nMin, si.nMax);
2531 /* Ditto, see above */
2532 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2533 memset(&si, 0, sizeof(si));
2534 si.cbSize = sizeof(si);
2535 si.fMask = SIF_PAGE | SIF_RANGE;
2536 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2537 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2538 "Vertical scrollbar is invisible, should be visible.\n");
2539 todo_wine {
2540 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2541 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2542 si.nPage, si.nMin, si.nMax);
2545 /* Ditto, see above */
2546 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2547 memset(&si, 0, sizeof(si));
2548 si.cbSize = sizeof(si);
2549 si.fMask = SIF_PAGE | SIF_RANGE;
2550 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2551 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2552 "Vertical scrollbar is invisible, should be visible.\n");
2553 todo_wine {
2554 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2555 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2556 si.nPage, si.nMin, si.nMax);
2559 /* Ditto, see above */
2560 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2561 memset(&si, 0, sizeof(si));
2562 si.cbSize = sizeof(si);
2563 si.fMask = SIF_PAGE | SIF_RANGE;
2564 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2565 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2566 "Vertical scrollbar is invisible, should be visible.\n");
2567 todo_wine {
2568 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2569 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2570 si.nPage, si.nMin, si.nMax);
2573 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2574 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2575 memset(&si, 0, sizeof(si));
2576 si.cbSize = sizeof(si);
2577 si.fMask = SIF_PAGE | SIF_RANGE;
2578 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2579 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2580 "Vertical scrollbar is visible, should be invisible.\n");
2581 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2582 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2583 si.nPage, si.nMin, si.nMax);
2585 DestroyWindow(hwndRichEdit);
2587 hwndRichEdit = new_richedit(NULL);
2589 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2590 memset(&si, 0, sizeof(si));
2591 si.cbSize = sizeof(si);
2592 si.fMask = SIF_PAGE | SIF_RANGE;
2593 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2594 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2595 "Vertical scrollbar is visible, should be invisible.\n");
2596 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2597 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2598 si.nPage, si.nMin, si.nMax);
2600 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2601 memset(&si, 0, sizeof(si));
2602 si.cbSize = sizeof(si);
2603 si.fMask = SIF_PAGE | SIF_RANGE;
2604 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2605 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2606 "Vertical scrollbar is visible, should be invisible.\n");
2607 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2608 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2609 si.nPage, si.nMin, si.nMax);
2611 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2612 memset(&si, 0, sizeof(si));
2613 si.cbSize = sizeof(si);
2614 si.fMask = SIF_PAGE | SIF_RANGE;
2615 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2616 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2617 "Vertical scrollbar is visible, should be invisible.\n");
2618 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2619 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2620 si.nPage, si.nMin, si.nMax);
2622 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2623 memset(&si, 0, sizeof(si));
2624 si.cbSize = sizeof(si);
2625 si.fMask = SIF_PAGE | SIF_RANGE;
2626 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2627 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2628 "Vertical scrollbar is visible, should be invisible.\n");
2629 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2630 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2631 si.nPage, si.nMin, si.nMax);
2633 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2634 memset(&si, 0, sizeof(si));
2635 si.cbSize = sizeof(si);
2636 si.fMask = SIF_PAGE | SIF_RANGE;
2637 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2638 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2639 "Vertical scrollbar is invisible, should be visible.\n");
2640 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2641 "reported page/range is %d (%d..%d)\n",
2642 si.nPage, si.nMin, si.nMax);
2644 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2645 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2646 memset(&si, 0, sizeof(si));
2647 si.cbSize = sizeof(si);
2648 si.fMask = SIF_PAGE | SIF_RANGE;
2649 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2650 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2651 "Vertical scrollbar is visible, should be invisible.\n");
2652 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2653 "reported page/range is %d (%d..%d)\n",
2654 si.nPage, si.nMin, si.nMax);
2656 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2657 memset(&si, 0, sizeof(si));
2658 si.cbSize = sizeof(si);
2659 si.fMask = SIF_PAGE | SIF_RANGE;
2660 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2661 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2662 "Vertical scrollbar is visible, should be invisible.\n");
2663 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2664 "reported page/range is %d (%d..%d)\n",
2665 si.nPage, si.nMin, si.nMax);
2667 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2668 EM_SCROLL will make visible any forcefully invisible scrollbar */
2669 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2670 memset(&si, 0, sizeof(si));
2671 si.cbSize = sizeof(si);
2672 si.fMask = SIF_PAGE | SIF_RANGE;
2673 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2674 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2675 "Vertical scrollbar is invisible, should be visible.\n");
2676 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2677 "reported page/range is %d (%d..%d)\n",
2678 si.nPage, si.nMin, si.nMax);
2680 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2681 memset(&si, 0, sizeof(si));
2682 si.cbSize = sizeof(si);
2683 si.fMask = SIF_PAGE | SIF_RANGE;
2684 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2685 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2686 "Vertical scrollbar is visible, should be invisible.\n");
2687 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2688 "reported page/range is %d (%d..%d)\n",
2689 si.nPage, si.nMin, si.nMax);
2691 /* Again, EM_SCROLL, with SB_LINEUP */
2692 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2693 memset(&si, 0, sizeof(si));
2694 si.cbSize = sizeof(si);
2695 si.fMask = SIF_PAGE | SIF_RANGE;
2696 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2697 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2698 "Vertical scrollbar is invisible, should be visible.\n");
2699 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2700 "reported page/range is %d (%d..%d)\n",
2701 si.nPage, si.nMin, si.nMax);
2703 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2704 memset(&si, 0, sizeof(si));
2705 si.cbSize = sizeof(si);
2706 si.fMask = SIF_PAGE | SIF_RANGE;
2707 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2708 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2709 "Vertical scrollbar is visible, should be invisible.\n");
2710 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2711 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2712 si.nPage, si.nMin, si.nMax);
2714 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2715 memset(&si, 0, sizeof(si));
2716 si.cbSize = sizeof(si);
2717 si.fMask = SIF_PAGE | SIF_RANGE;
2718 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2719 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2720 "Vertical scrollbar is invisible, should be visible.\n");
2721 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2722 "reported page/range is %d (%d..%d)\n",
2723 si.nPage, si.nMin, si.nMax);
2725 DestroyWindow(hwndRichEdit);
2728 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2729 hwndRichEdit = new_richedit(NULL);
2731 #define ENABLE_WS_VSCROLL(hwnd) \
2732 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2733 #define DISABLE_WS_VSCROLL(hwnd) \
2734 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2736 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2737 ENABLE_WS_VSCROLL(hwndRichEdit);
2738 memset(&si, 0, sizeof(si));
2739 si.cbSize = sizeof(si);
2740 si.fMask = SIF_PAGE | SIF_RANGE;
2741 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2742 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2743 "Vertical scrollbar is invisible, should be visible.\n");
2744 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2745 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2746 si.nPage, si.nMin, si.nMax);
2748 /* Ditto, see above */
2749 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
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 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2757 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2758 si.nPage, si.nMin, si.nMax);
2760 /* Ditto, see above */
2761 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2762 memset(&si, 0, sizeof(si));
2763 si.cbSize = sizeof(si);
2764 si.fMask = SIF_PAGE | SIF_RANGE;
2765 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2766 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2767 "Vertical scrollbar is invisible, should be visible.\n");
2768 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2769 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2770 si.nPage, si.nMin, si.nMax);
2772 /* Ditto, see above */
2773 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2774 memset(&si, 0, sizeof(si));
2775 si.cbSize = sizeof(si);
2776 si.fMask = SIF_PAGE | SIF_RANGE;
2777 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2778 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2779 "Vertical scrollbar is invisible, should be visible.\n");
2780 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2781 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2782 si.nPage, si.nMin, si.nMax);
2784 /* Ditto, see above */
2785 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2786 memset(&si, 0, sizeof(si));
2787 si.cbSize = sizeof(si);
2788 si.fMask = SIF_PAGE | SIF_RANGE;
2789 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2790 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2791 "Vertical scrollbar is invisible, should be visible.\n");
2792 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2793 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2794 si.nPage, si.nMin, si.nMax);
2796 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2797 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2798 memset(&si, 0, sizeof(si));
2799 si.cbSize = sizeof(si);
2800 si.fMask = SIF_PAGE | SIF_RANGE;
2801 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2802 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2803 "Vertical scrollbar is visible, should be invisible.\n");
2804 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2805 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2806 si.nPage, si.nMin, si.nMax);
2808 DestroyWindow(hwndRichEdit);
2810 hwndRichEdit = new_richedit(NULL);
2812 DISABLE_WS_VSCROLL(hwndRichEdit);
2813 memset(&si, 0, sizeof(si));
2814 si.cbSize = sizeof(si);
2815 si.fMask = SIF_PAGE | SIF_RANGE;
2816 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2817 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2818 "Vertical scrollbar is visible, should be invisible.\n");
2819 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2820 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2821 si.nPage, si.nMin, si.nMax);
2823 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2824 memset(&si, 0, sizeof(si));
2825 si.cbSize = sizeof(si);
2826 si.fMask = SIF_PAGE | SIF_RANGE;
2827 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2828 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2829 "Vertical scrollbar is visible, should be invisible.\n");
2830 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2831 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2832 si.nPage, si.nMin, si.nMax);
2834 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2835 memset(&si, 0, sizeof(si));
2836 si.cbSize = sizeof(si);
2837 si.fMask = SIF_PAGE | SIF_RANGE;
2838 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2839 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2840 "Vertical scrollbar is visible, should be invisible.\n");
2841 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2842 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2843 si.nPage, si.nMin, si.nMax);
2845 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2846 memset(&si, 0, sizeof(si));
2847 si.cbSize = sizeof(si);
2848 si.fMask = SIF_PAGE | SIF_RANGE;
2849 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2850 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2851 "Vertical scrollbar is visible, should be invisible.\n");
2852 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2853 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2854 si.nPage, si.nMin, si.nMax);
2856 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2857 memset(&si, 0, sizeof(si));
2858 si.cbSize = sizeof(si);
2859 si.fMask = SIF_PAGE | SIF_RANGE;
2860 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2861 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2862 "Vertical scrollbar is invisible, should be visible.\n");
2863 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2864 "reported page/range is %d (%d..%d)\n",
2865 si.nPage, si.nMin, si.nMax);
2867 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2868 DISABLE_WS_VSCROLL(hwndRichEdit);
2869 memset(&si, 0, sizeof(si));
2870 si.cbSize = sizeof(si);
2871 si.fMask = SIF_PAGE | SIF_RANGE;
2872 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2873 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2874 "Vertical scrollbar is visible, should be invisible.\n");
2875 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2876 "reported page/range is %d (%d..%d)\n",
2877 si.nPage, si.nMin, si.nMax);
2879 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2880 memset(&si, 0, sizeof(si));
2881 si.cbSize = sizeof(si);
2882 si.fMask = SIF_PAGE | SIF_RANGE;
2883 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2884 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2885 "Vertical scrollbar is visible, should be invisible.\n");
2886 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2887 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2888 si.nPage, si.nMin, si.nMax);
2890 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2891 memset(&si, 0, sizeof(si));
2892 si.cbSize = sizeof(si);
2893 si.fMask = SIF_PAGE | SIF_RANGE;
2894 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2895 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2896 "Vertical scrollbar is invisible, should be visible.\n");
2897 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2898 "reported page/range is %d (%d..%d)\n",
2899 si.nPage, si.nMin, si.nMax);
2901 DISABLE_WS_VSCROLL(hwndRichEdit);
2902 memset(&si, 0, sizeof(si));
2903 si.cbSize = sizeof(si);
2904 si.fMask = SIF_PAGE | SIF_RANGE;
2905 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2906 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2907 "Vertical scrollbar is visible, should be invisible.\n");
2908 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2909 "reported page/range is %d (%d..%d)\n",
2910 si.nPage, si.nMin, si.nMax);
2912 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2913 EM_SCROLL will make visible any forcefully invisible scrollbar */
2914 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2915 memset(&si, 0, sizeof(si));
2916 si.cbSize = sizeof(si);
2917 si.fMask = SIF_PAGE | SIF_RANGE;
2918 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2919 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2920 "Vertical scrollbar is invisible, should be visible.\n");
2921 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2922 "reported page/range is %d (%d..%d)\n",
2923 si.nPage, si.nMin, si.nMax);
2925 DISABLE_WS_VSCROLL(hwndRichEdit);
2926 memset(&si, 0, sizeof(si));
2927 si.cbSize = sizeof(si);
2928 si.fMask = SIF_PAGE | SIF_RANGE;
2929 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2930 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2931 "Vertical scrollbar is visible, should be invisible.\n");
2932 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2933 "reported page/range is %d (%d..%d)\n",
2934 si.nPage, si.nMin, si.nMax);
2936 /* Again, EM_SCROLL, with SB_LINEUP */
2937 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2938 memset(&si, 0, sizeof(si));
2939 si.cbSize = sizeof(si);
2940 si.fMask = SIF_PAGE | SIF_RANGE;
2941 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2942 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2943 "Vertical scrollbar is invisible, should be visible.\n");
2944 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2945 "reported page/range is %d (%d..%d)\n",
2946 si.nPage, si.nMin, si.nMax);
2948 DestroyWindow(hwndRichEdit);
2950 /* This window proc models what is going on with Corman Lisp 3.0.
2951 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
2952 force the scrollbar into visibility. Recursion should NOT happen
2953 as a result of this action.
2955 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
2956 if (r) {
2957 richeditProc = cls.lpfnWndProc;
2958 cls.lpfnWndProc = RicheditStupidOverrideProcA;
2959 cls.lpszClassName = "RicheditStupidOverride";
2960 if(!RegisterClassA(&cls)) assert(0);
2962 recursionLevel = 0;
2963 WM_SIZE_recursionLevel = 0;
2964 bailedOutOfRecursion = FALSE;
2965 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
2966 ok(!bailedOutOfRecursion,
2967 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2969 recursionLevel = 0;
2970 WM_SIZE_recursionLevel = 0;
2971 bailedOutOfRecursion = FALSE;
2972 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
2973 ok(!bailedOutOfRecursion,
2974 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2976 /* Unblock window in order to process WM_DESTROY */
2977 recursionLevel = 0;
2978 bailedOutOfRecursion = FALSE;
2979 WM_SIZE_recursionLevel = 0;
2980 DestroyWindow(hwndRichEdit);
2984 static void test_EM_SETUNDOLIMIT(void)
2986 /* cases we test for:
2987 * default behaviour - limiting at 100 undo's
2988 * undo disabled - setting a limit of 0
2989 * undo limited - undo limit set to some to some number, like 2
2990 * bad input - sending a negative number should default to 100 undo's */
2992 HWND hwndRichEdit = new_richedit(NULL);
2993 CHARRANGE cr;
2994 int i;
2995 int result;
2997 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2998 cr.cpMin = 0;
2999 cr.cpMax = 1;
3000 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3001 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3002 also, multiple pastes don't combine like WM_CHAR would */
3003 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3005 /* first case - check the default */
3006 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3007 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3008 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3009 for (i=0; i<100; i++) /* Undo 100 of them */
3010 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3011 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3012 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3014 /* second case - cannot undo */
3015 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3016 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3017 SendMessage(hwndRichEdit,
3018 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3019 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3020 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3022 /* third case - set it to an arbitrary number */
3023 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3024 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3025 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3026 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3027 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3028 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3029 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3030 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3031 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3032 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3033 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3034 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3035 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3036 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3038 /* fourth case - setting negative numbers should default to 100 undos */
3039 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3040 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3041 ok (result == 100,
3042 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3044 DestroyWindow(hwndRichEdit);
3047 static void test_ES_PASSWORD(void)
3049 /* This isn't hugely testable, so we're just going to run it through its paces */
3051 HWND hwndRichEdit = new_richedit(NULL);
3052 WCHAR result;
3054 /* First, check the default of a regular control */
3055 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3056 ok (result == 0,
3057 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3059 /* Now, set it to something normal */
3060 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3061 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3062 ok (result == 120,
3063 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3065 /* Now, set it to something odd */
3066 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3067 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3068 ok (result == 1234,
3069 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3070 DestroyWindow(hwndRichEdit);
3073 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3074 LPBYTE pbBuff,
3075 LONG cb,
3076 LONG *pcb)
3078 char** str = (char**)dwCookie;
3079 *pcb = cb;
3080 if (*pcb > 0) {
3081 memcpy(*str, pbBuff, *pcb);
3082 *str += *pcb;
3084 return 0;
3087 static void test_WM_SETTEXT()
3089 HWND hwndRichEdit = new_richedit(NULL);
3090 const char * TestItem1 = "TestSomeText";
3091 const char * TestItem2 = "TestSomeText\r";
3092 const char * TestItem2_after = "TestSomeText\r\n";
3093 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3094 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3095 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3096 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3097 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3098 const char * TestItem5_after = "TestSomeText TestSomeText";
3099 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3100 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3101 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3102 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3104 char buf[1024] = {0};
3105 LRESULT result;
3106 EDITSTREAM es;
3107 char * p;
3109 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3110 any solitary \r to be converted to \r\n on return. Properly paired
3111 \r\n are not affected. It also shows that the special sequence \r\r\n
3112 gets converted to a single space.
3115 #define TEST_SETTEXT(a, b) \
3116 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3117 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3118 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3119 ok (result == lstrlen(buf), \
3120 "WM_GETTEXT returned %ld instead of expected %u\n", \
3121 result, lstrlen(buf)); \
3122 result = strcmp(b, buf); \
3123 ok(result == 0, \
3124 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3126 TEST_SETTEXT(TestItem1, TestItem1)
3127 TEST_SETTEXT(TestItem2, TestItem2_after)
3128 TEST_SETTEXT(TestItem3, TestItem3_after)
3129 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3130 TEST_SETTEXT(TestItem4, TestItem4_after)
3131 TEST_SETTEXT(TestItem5, TestItem5_after)
3132 TEST_SETTEXT(TestItem6, TestItem6_after)
3133 TEST_SETTEXT(TestItem7, TestItem7_after)
3135 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3136 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3137 p = buf;
3138 es.dwCookie = (DWORD_PTR)&p;
3139 es.dwError = 0;
3140 es.pfnCallback = test_WM_SETTEXT_esCallback;
3141 memset(buf, 0, sizeof(buf));
3142 SendMessage(hwndRichEdit, EM_STREAMOUT,
3143 (WPARAM)(SF_RTF), (LPARAM)&es);
3144 trace("EM_STREAMOUT produced: \n%s\n", buf);
3145 TEST_SETTEXT(buf, TestItem1)
3147 #undef TEST_SETTEXT
3148 DestroyWindow(hwndRichEdit);
3151 static void test_EM_STREAMOUT(void)
3153 HWND hwndRichEdit = new_richedit(NULL);
3154 int r;
3155 EDITSTREAM es;
3156 char buf[1024] = {0};
3157 char * p;
3159 const char * TestItem1 = "TestSomeText";
3160 const char * TestItem2 = "TestSomeText\r";
3161 const char * TestItem3 = "TestSomeText\r\n";
3163 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3164 p = buf;
3165 es.dwCookie = (DWORD_PTR)&p;
3166 es.dwError = 0;
3167 es.pfnCallback = test_WM_SETTEXT_esCallback;
3168 memset(buf, 0, sizeof(buf));
3169 SendMessage(hwndRichEdit, EM_STREAMOUT,
3170 (WPARAM)(SF_TEXT), (LPARAM)&es);
3171 r = strlen(buf);
3172 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3173 ok(strcmp(buf, TestItem1) == 0,
3174 "streamed text different, got %s\n", buf);
3176 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3177 p = buf;
3178 es.dwCookie = (DWORD_PTR)&p;
3179 es.dwError = 0;
3180 es.pfnCallback = test_WM_SETTEXT_esCallback;
3181 memset(buf, 0, sizeof(buf));
3182 SendMessage(hwndRichEdit, EM_STREAMOUT,
3183 (WPARAM)(SF_TEXT), (LPARAM)&es);
3184 r = strlen(buf);
3185 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3186 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3187 ok(strcmp(buf, TestItem3) == 0,
3188 "streamed text different from, got %s\n", buf);
3189 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3190 p = buf;
3191 es.dwCookie = (DWORD_PTR)&p;
3192 es.dwError = 0;
3193 es.pfnCallback = test_WM_SETTEXT_esCallback;
3194 memset(buf, 0, sizeof(buf));
3195 SendMessage(hwndRichEdit, EM_STREAMOUT,
3196 (WPARAM)(SF_TEXT), (LPARAM)&es);
3197 r = strlen(buf);
3198 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3199 ok(strcmp(buf, TestItem3) == 0,
3200 "streamed text different, got %s\n", buf);
3202 DestroyWindow(hwndRichEdit);
3205 static void test_EM_SETTEXTEX(void)
3207 HWND hwndRichEdit = new_richedit(NULL);
3208 SETTEXTEX setText;
3209 GETTEXTEX getText;
3210 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3211 'S', 'o', 'm', 'e',
3212 'T', 'e', 'x', 't', 0};
3213 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3214 't', 'S', 'o', 'm',
3215 'e', 'T', 'e', 'x',
3216 't', 't', 'S', 'o',
3217 'm', 'e', 'T', 'e',
3218 'x', 't', 0};
3219 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3220 '\n','t','S','o','m','e','T','e','x','t',0};
3221 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3222 'S', 'o', 'm', 'e',
3223 'T', 'e', 'x', 't',
3224 '\r', 0};
3225 const char * TestItem2_after = "TestSomeText\r\n";
3226 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3227 'S', 'o', 'm', 'e',
3228 'T', 'e', 'x', 't',
3229 '\r','\n','\r','\n', 0};
3230 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3231 'S', 'o', 'm', 'e',
3232 'T', 'e', 'x', 't',
3233 '\n','\n', 0};
3234 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3235 'S', 'o', 'm', 'e',
3236 'T', 'e', 'x', 't',
3237 '\r','\r', 0};
3238 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3239 'S', 'o', 'm', 'e',
3240 'T', 'e', 'x', 't',
3241 '\r','\r','\n','\r',
3242 '\n', 0};
3243 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3244 'S', 'o', 'm', 'e',
3245 'T', 'e', 'x', 't',
3246 ' ','\r', 0};
3247 #define MAX_BUF_LEN 1024
3248 WCHAR buf[MAX_BUF_LEN];
3249 char bufACP[MAX_BUF_LEN];
3250 char * p;
3251 int result;
3252 CHARRANGE cr;
3253 EDITSTREAM es;
3255 setText.codepage = 1200; /* no constant for unicode */
3256 getText.codepage = 1200; /* no constant for unicode */
3257 getText.cb = MAX_BUF_LEN;
3258 getText.flags = GT_DEFAULT;
3259 getText.lpDefaultChar = NULL;
3260 getText.lpUsedDefChar = NULL;
3262 setText.flags = 0;
3263 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3264 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3265 ok(lstrcmpW(buf, TestItem1) == 0,
3266 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3268 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3269 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3271 setText.codepage = 1200; /* no constant for unicode */
3272 getText.codepage = 1200; /* no constant for unicode */
3273 getText.cb = MAX_BUF_LEN;
3274 getText.flags = GT_DEFAULT;
3275 getText.lpDefaultChar = NULL;
3276 getText.lpUsedDefChar = NULL;
3277 setText.flags = 0;
3278 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3279 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3280 ok(lstrcmpW(buf, TestItem2) == 0,
3281 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3283 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3284 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3285 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3286 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3288 /* Baseline test for just-enough buffer space for string */
3289 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3290 getText.codepage = 1200; /* no constant for unicode */
3291 getText.flags = GT_DEFAULT;
3292 getText.lpDefaultChar = NULL;
3293 getText.lpUsedDefChar = NULL;
3294 memset(buf, 0, MAX_BUF_LEN);
3295 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3296 ok(lstrcmpW(buf, TestItem2) == 0,
3297 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3299 /* When there is enough space for one character, but not both, of the CRLF
3300 pair at the end of the string, the CR is not copied at all. That is,
3301 the caller must not see CRLF pairs truncated to CR at the end of the
3302 string.
3304 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3305 getText.codepage = 1200; /* no constant for unicode */
3306 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3307 getText.lpDefaultChar = NULL;
3308 getText.lpUsedDefChar = NULL;
3309 memset(buf, 0, MAX_BUF_LEN);
3310 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3311 ok(lstrcmpW(buf, TestItem1) == 0,
3312 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3315 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3316 setText.codepage = 1200; /* no constant for unicode */
3317 getText.codepage = 1200; /* no constant for unicode */
3318 getText.cb = MAX_BUF_LEN;
3319 getText.flags = GT_DEFAULT;
3320 getText.lpDefaultChar = NULL;
3321 getText.lpUsedDefChar = NULL;
3322 setText.flags = 0;
3323 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3324 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3325 ok(lstrcmpW(buf, TestItem3_after) == 0,
3326 "EM_SETTEXTEX did not convert properly\n");
3328 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3329 setText.codepage = 1200; /* no constant for unicode */
3330 getText.codepage = 1200; /* no constant for unicode */
3331 getText.cb = MAX_BUF_LEN;
3332 getText.flags = GT_DEFAULT;
3333 getText.lpDefaultChar = NULL;
3334 getText.lpUsedDefChar = NULL;
3335 setText.flags = 0;
3336 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3337 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3338 ok(lstrcmpW(buf, TestItem3_after) == 0,
3339 "EM_SETTEXTEX did not convert properly\n");
3341 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3342 setText.codepage = 1200; /* no constant for unicode */
3343 getText.codepage = 1200; /* no constant for unicode */
3344 getText.cb = MAX_BUF_LEN;
3345 getText.flags = GT_DEFAULT;
3346 getText.lpDefaultChar = NULL;
3347 getText.lpUsedDefChar = NULL;
3348 setText.flags = 0;
3349 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3350 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3351 ok(lstrcmpW(buf, TestItem4_after) == 0,
3352 "EM_SETTEXTEX did not convert properly\n");
3354 /* !ST_SELECTION && Unicode && !\rtf */
3355 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3356 (WPARAM)&setText, (LPARAM) NULL);
3357 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3359 ok (result == 1,
3360 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3361 ok(lstrlenW(buf) == 0,
3362 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3364 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3365 setText.flags = 0;
3366 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3367 /* select some text */
3368 cr.cpMax = 1;
3369 cr.cpMin = 3;
3370 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3371 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3372 setText.flags = ST_SELECTION;
3373 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3374 (WPARAM)&setText, (LPARAM) NULL);
3375 ok(result == 0,
3376 "EM_SETTEXTEX with NULL lParam to replace selection"
3377 " with no text should return 0. Got %i\n",
3378 result);
3380 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3381 setText.flags = 0;
3382 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3383 /* select some text */
3384 cr.cpMax = 1;
3385 cr.cpMin = 3;
3386 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3387 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3388 setText.flags = ST_SELECTION;
3389 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3390 (WPARAM)&setText, (LPARAM) TestItem1);
3391 /* get text */
3392 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3393 ok(result == lstrlenW(TestItem1),
3394 "EM_SETTEXTEX with NULL lParam to replace selection"
3395 " with no text should return 0. Got %i\n",
3396 result);
3397 ok(lstrlenW(buf) == 22,
3398 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3399 lstrlenW(buf) );
3401 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3402 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3403 p = (char *)buf;
3404 es.dwCookie = (DWORD_PTR)&p;
3405 es.dwError = 0;
3406 es.pfnCallback = test_WM_SETTEXT_esCallback;
3407 memset(buf, 0, sizeof(buf));
3408 SendMessage(hwndRichEdit, EM_STREAMOUT,
3409 (WPARAM)(SF_RTF), (LPARAM)&es);
3410 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3412 /* !ST_SELECTION && !Unicode && \rtf */
3413 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3414 getText.codepage = 1200; /* no constant for unicode */
3415 getText.cb = MAX_BUF_LEN;
3416 getText.flags = GT_DEFAULT;
3417 getText.lpDefaultChar = NULL;
3418 getText.lpUsedDefChar = NULL;
3420 setText.flags = 0;
3421 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3422 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3423 ok(lstrcmpW(buf, TestItem1) == 0,
3424 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3426 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3427 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3428 p = (char *)buf;
3429 es.dwCookie = (DWORD_PTR)&p;
3430 es.dwError = 0;
3431 es.pfnCallback = test_WM_SETTEXT_esCallback;
3432 memset(buf, 0, sizeof(buf));
3433 SendMessage(hwndRichEdit, EM_STREAMOUT,
3434 (WPARAM)(SF_RTF), (LPARAM)&es);
3435 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3437 /* select some text */
3438 cr.cpMax = 1;
3439 cr.cpMin = 3;
3440 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3442 /* ST_SELECTION && !Unicode && \rtf */
3443 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3444 getText.codepage = 1200; /* no constant for unicode */
3445 getText.cb = MAX_BUF_LEN;
3446 getText.flags = GT_DEFAULT;
3447 getText.lpDefaultChar = NULL;
3448 getText.lpUsedDefChar = NULL;
3450 setText.flags = ST_SELECTION;
3451 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3452 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3453 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3455 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3456 setText.codepage = 1200; /* no constant for unicode */
3457 getText.codepage = CP_ACP;
3458 getText.cb = MAX_BUF_LEN;
3460 setText.flags = 0;
3461 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3462 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3464 /* select some text */
3465 cr.cpMax = 1;
3466 cr.cpMin = 3;
3467 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3469 /* ST_SELECTION && !Unicode && !\rtf */
3470 setText.codepage = CP_ACP;
3471 getText.codepage = 1200; /* no constant for unicode */
3472 getText.cb = MAX_BUF_LEN;
3473 getText.flags = GT_DEFAULT;
3474 getText.lpDefaultChar = NULL;
3475 getText.lpUsedDefChar = NULL;
3477 setText.flags = ST_SELECTION;
3478 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3479 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3480 ok(lstrcmpW(buf, TestItem1alt) == 0,
3481 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3482 " using ST_SELECTION and non-Unicode\n");
3485 DestroyWindow(hwndRichEdit);
3488 static void test_EM_LIMITTEXT(void)
3490 int ret;
3492 HWND hwndRichEdit = new_richedit(NULL);
3494 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3495 * about setting the length to -1 for multiline edit controls doesn't happen.
3498 /* Don't check default gettextlimit case. That's done in other tests */
3500 /* Set textlimit to 100 */
3501 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3502 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3503 ok (ret == 100,
3504 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3506 /* Set textlimit to 0 */
3507 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3508 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3509 ok (ret == 65536,
3510 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3512 /* Set textlimit to -1 */
3513 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3514 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3515 ok (ret == -1,
3516 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3518 /* Set textlimit to -2 */
3519 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3520 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3521 ok (ret == -2,
3522 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3524 DestroyWindow (hwndRichEdit);
3528 static void test_EM_EXLIMITTEXT(void)
3530 int i, selBegin, selEnd, len1, len2;
3531 int result;
3532 char text[1024 + 1];
3533 char buffer[1024 + 1];
3534 int textlimit = 0; /* multiple of 100 */
3535 HWND hwndRichEdit = new_richedit(NULL);
3537 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3538 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3540 textlimit = 256000;
3541 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3542 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3543 /* set higher */
3544 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3546 textlimit = 1000;
3547 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3548 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3549 /* set lower */
3550 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3552 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3553 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3554 /* default for WParam = 0 */
3555 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3557 textlimit = sizeof(text)-1;
3558 memset(text, 'W', textlimit);
3559 text[sizeof(text)-1] = 0;
3560 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3561 /* maxed out text */
3562 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3564 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3565 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3566 len1 = selEnd - selBegin;
3568 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3569 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3570 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3571 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3572 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3573 len2 = selEnd - selBegin;
3575 ok(len1 != len2,
3576 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3577 len1,len2,i);
3579 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3580 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3581 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3582 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3583 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3584 len1 = selEnd - selBegin;
3586 ok(len1 != len2,
3587 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3588 len1,len2,i);
3590 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3591 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3592 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3593 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3594 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3595 len2 = selEnd - selBegin;
3597 ok(len1 == len2,
3598 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3599 len1,len2,i);
3601 /* set text up to the limit, select all the text, then add a char */
3602 textlimit = 5;
3603 memset(text, 'W', textlimit);
3604 text[textlimit] = 0;
3605 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3606 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3607 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3608 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3609 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3610 result = strcmp(buffer, "A");
3611 ok(0 == result, "got string = \"%s\"\n", buffer);
3613 /* WM_SETTEXT not limited */
3614 textlimit = 10;
3615 memset(text, 'W', textlimit);
3616 text[textlimit] = 0;
3617 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3618 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3619 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3620 i = strlen(buffer);
3621 ok(10 == i, "expected 10 chars\n");
3622 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3623 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3625 /* try inserting more text at end */
3626 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3627 ok(0 == i, "WM_CHAR wasn't processed\n");
3628 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3629 i = strlen(buffer);
3630 ok(10 == i, "expected 10 chars, got %i\n", i);
3631 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3632 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3634 /* try inserting text at beginning */
3635 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3636 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3637 ok(0 == i, "WM_CHAR wasn't processed\n");
3638 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3639 i = strlen(buffer);
3640 ok(10 == i, "expected 10 chars, got %i\n", i);
3641 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3642 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3644 /* WM_CHAR is limited */
3645 textlimit = 1;
3646 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3647 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3648 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3649 ok(0 == i, "WM_CHAR wasn't processed\n");
3650 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3651 ok(0 == i, "WM_CHAR wasn't processed\n");
3652 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3653 i = strlen(buffer);
3654 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3656 DestroyWindow(hwndRichEdit);
3659 static void test_EM_GETLIMITTEXT(void)
3661 int i;
3662 HWND hwndRichEdit = new_richedit(NULL);
3664 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3665 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3667 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3668 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3669 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3671 DestroyWindow(hwndRichEdit);
3674 static void test_WM_SETFONT(void)
3676 /* There is no invalid input or error conditions for this function.
3677 * NULL wParam and lParam just fall back to their default values
3678 * It should be noted that even if you use a gibberish name for your fonts
3679 * here, it will still work because the name is stored. They will display as
3680 * System, but will report their name to be whatever they were created as */
3682 HWND hwndRichEdit = new_richedit(NULL);
3683 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3684 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3685 FF_DONTCARE, "Marlett");
3686 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3687 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3688 FF_DONTCARE, "MS Sans Serif");
3689 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3690 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3691 FF_DONTCARE, "Courier");
3692 LOGFONTA sentLogFont;
3693 CHARFORMAT2A returnedCF2A;
3695 returnedCF2A.cbSize = sizeof(returnedCF2A);
3697 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3698 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
3699 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3701 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3702 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3703 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3704 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3706 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
3707 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3708 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3709 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3710 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3711 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3713 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
3714 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3715 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3716 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3717 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3718 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3720 /* This last test is special since we send in NULL. We clear the variables
3721 * and just compare to "System" instead of the sent in font name. */
3722 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3723 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3724 returnedCF2A.cbSize = sizeof(returnedCF2A);
3726 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
3727 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3728 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3729 ok (!strcmp("System",returnedCF2A.szFaceName),
3730 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3732 DestroyWindow(hwndRichEdit);
3736 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3737 LPBYTE pbBuff,
3738 LONG cb,
3739 LONG *pcb)
3741 const char** str = (const char**)dwCookie;
3742 int size = strlen(*str);
3743 if(size > 3) /* let's make it piecemeal for fun */
3744 size = 3;
3745 *pcb = cb;
3746 if (*pcb > size) {
3747 *pcb = size;
3749 if (*pcb > 0) {
3750 memcpy(pbBuff, *str, *pcb);
3751 *str += *pcb;
3753 return 0;
3756 static void test_EM_GETMODIFY(void)
3758 HWND hwndRichEdit = new_richedit(NULL);
3759 LRESULT result;
3760 SETTEXTEX setText;
3761 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3762 'S', 'o', 'm', 'e',
3763 'T', 'e', 'x', 't', 0};
3764 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3765 'S', 'o', 'm', 'e',
3766 'O', 't', 'h', 'e', 'r',
3767 'T', 'e', 'x', 't', 0};
3768 const char* streamText = "hello world";
3769 CHARFORMAT2 cf2;
3770 PARAFORMAT2 pf2;
3771 EDITSTREAM es;
3773 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3774 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3775 FF_DONTCARE, "Courier");
3777 setText.codepage = 1200; /* no constant for unicode */
3778 setText.flags = ST_KEEPUNDO;
3781 /* modify flag shouldn't be set when richedit is first created */
3782 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3783 ok (result == 0,
3784 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
3786 /* setting modify flag should actually set it */
3787 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
3788 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3789 ok (result != 0,
3790 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
3792 /* clearing modify flag should actually clear it */
3793 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3794 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3795 ok (result == 0,
3796 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
3798 /* setting font doesn't change modify flag */
3799 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3800 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
3801 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3802 ok (result == 0,
3803 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
3805 /* setting text should set modify flag */
3806 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3807 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3808 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3809 ok (result != 0,
3810 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
3812 /* undo previous text doesn't reset modify flag */
3813 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3814 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3815 ok (result != 0,
3816 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
3818 /* set text with no flag to keep undo stack should not set modify flag */
3819 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3820 setText.flags = 0;
3821 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3822 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3823 ok (result == 0,
3824 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
3826 /* WM_SETTEXT doesn't modify */
3827 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3828 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3829 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3830 ok (result == 0,
3831 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3833 /* clear the text */
3834 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3835 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3836 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3837 ok (result == 0,
3838 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3840 /* replace text */
3841 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3842 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3843 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3844 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3845 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3846 ok (result != 0,
3847 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3849 /* copy/paste text 1 */
3850 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3851 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3852 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3853 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3854 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3855 ok (result != 0,
3856 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3858 /* copy/paste text 2 */
3859 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3860 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3861 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3862 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3863 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3864 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3865 ok (result != 0,
3866 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3868 /* press char */
3869 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3870 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3871 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3872 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3873 ok (result != 0,
3874 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3876 /* press del */
3877 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3878 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3879 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
3880 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3881 ok (result != 0,
3882 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3884 /* set char format */
3885 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3886 cf2.cbSize = sizeof(CHARFORMAT2);
3887 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3888 (LPARAM) &cf2);
3889 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3890 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3891 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3892 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3893 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3894 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3895 ok (result != 0,
3896 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3898 /* set para format */
3899 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3900 pf2.cbSize = sizeof(PARAFORMAT2);
3901 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3902 (LPARAM) &pf2);
3903 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3904 pf2.wAlignment = PFA_RIGHT;
3905 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3906 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3907 ok (result == 0,
3908 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3910 /* EM_STREAM */
3911 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3912 es.dwCookie = (DWORD_PTR)&streamText;
3913 es.dwError = 0;
3914 es.pfnCallback = test_EM_GETMODIFY_esCallback;
3915 SendMessage(hwndRichEdit, EM_STREAMIN,
3916 (WPARAM)(SF_TEXT), (LPARAM)&es);
3917 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3918 ok (result != 0,
3919 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
3921 DestroyWindow(hwndRichEdit);
3924 struct exsetsel_s {
3925 long min;
3926 long max;
3927 long expected_retval;
3928 int expected_getsel_start;
3929 int expected_getsel_end;
3930 int _exsetsel_todo_wine;
3931 int _getsel_todo_wine;
3934 const struct exsetsel_s exsetsel_tests[] = {
3935 /* sanity tests */
3936 {5, 10, 10, 5, 10, 0, 0},
3937 {15, 17, 17, 15, 17, 0, 0},
3938 /* test cpMax > strlen() */
3939 {0, 100, 18, 0, 18, 0, 1},
3940 /* test cpMin == cpMax */
3941 {5, 5, 5, 5, 5, 0, 0},
3942 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
3943 {-1, 0, 5, 5, 5, 0, 0},
3944 {-1, 17, 5, 5, 5, 0, 0},
3945 {-1, 18, 5, 5, 5, 0, 0},
3946 /* test cpMin < 0 && cpMax < 0 */
3947 {-1, -1, 17, 17, 17, 0, 0},
3948 {-4, -5, 17, 17, 17, 0, 0},
3949 /* test cMin >=0 && cpMax < 0 (bug 6814) */
3950 {0, -1, 18, 0, 18, 0, 1},
3951 {17, -5, 18, 17, 18, 0, 1},
3952 {18, -3, 17, 17, 17, 0, 0},
3953 /* test if cpMin > cpMax */
3954 {15, 19, 18, 15, 18, 0, 1},
3955 {19, 15, 18, 15, 18, 0, 1}
3958 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
3959 CHARRANGE cr;
3960 long result;
3961 int start, end;
3963 cr.cpMin = setsel->min;
3964 cr.cpMax = setsel->max;
3965 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
3967 if (setsel->_exsetsel_todo_wine) {
3968 todo_wine {
3969 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3971 } else {
3972 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3975 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
3977 if (setsel->_getsel_todo_wine) {
3978 todo_wine {
3979 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);
3981 } else {
3982 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);
3986 static void test_EM_EXSETSEL(void)
3988 HWND hwndRichEdit = new_richedit(NULL);
3989 int i;
3990 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
3992 /* sending some text to the window */
3993 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3994 /* 01234567890123456*/
3995 /* 10 */
3997 for (i = 0; i < num_tests; i++) {
3998 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4001 DestroyWindow(hwndRichEdit);
4004 static void test_EM_REPLACESEL(int redraw)
4006 HWND hwndRichEdit = new_richedit(NULL);
4007 char buffer[1024] = {0};
4008 int r;
4009 GETTEXTEX getText;
4010 CHARRANGE cr;
4012 /* sending some text to the window */
4013 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4014 /* 01234567890123456*/
4015 /* 10 */
4017 /* FIXME add more tests */
4018 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4019 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
4020 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4021 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4022 r = strcmp(buffer, "testing");
4023 ok(0 == r, "expected %d, got %d\n", 0, r);
4025 DestroyWindow(hwndRichEdit);
4027 hwndRichEdit = new_richedit(NULL);
4029 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4030 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4032 /* Test behavior with carriage returns and newlines */
4033 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4034 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4035 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4036 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4037 r = strcmp(buffer, "RichEdit1");
4038 ok(0 == r, "expected %d, got %d\n", 0, r);
4039 getText.cb = 1024;
4040 getText.codepage = CP_ACP;
4041 getText.flags = GT_DEFAULT;
4042 getText.lpDefaultChar = NULL;
4043 getText.lpUsedDefChar = NULL;
4044 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4045 ok(strcmp(buffer, "RichEdit1") == 0,
4046 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4048 /* Test number of lines reported after EM_REPLACESEL */
4049 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4050 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4052 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4053 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4054 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4055 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4056 r = strcmp(buffer, "RichEdit1\r\n");
4057 ok(0 == r, "expected %d, got %d\n", 0, r);
4058 getText.cb = 1024;
4059 getText.codepage = CP_ACP;
4060 getText.flags = GT_DEFAULT;
4061 getText.lpDefaultChar = NULL;
4062 getText.lpUsedDefChar = NULL;
4063 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4064 ok(strcmp(buffer, "RichEdit1\r") == 0,
4065 "EM_GETTEXTEX returned incorrect string\n");
4067 /* Test number of lines reported after EM_REPLACESEL */
4068 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4069 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4071 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4072 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4073 returns the number of characters *inserted* into the control (after
4074 required conversions), but WinXP's riched20 returns the number of
4075 characters interpreted from the original lParam. Wine's builtin riched20
4076 implements the WinXP behavior.
4078 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4079 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4080 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4081 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4083 /* Test number of lines reported after EM_REPLACESEL */
4084 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4085 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4087 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4088 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4089 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4090 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4092 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4093 r = strcmp(buffer, "RichEdit1\r\n");
4094 ok(0 == r, "expected %d, got %d\n", 0, r);
4095 getText.cb = 1024;
4096 getText.codepage = CP_ACP;
4097 getText.flags = GT_DEFAULT;
4098 getText.lpDefaultChar = NULL;
4099 getText.lpUsedDefChar = NULL;
4100 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4101 ok(strcmp(buffer, "RichEdit1\r") == 0,
4102 "EM_GETTEXTEX returned incorrect string\n");
4104 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4105 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4106 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4107 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4109 /* The following tests show that richedit should handle the special \r\r\n
4110 sequence by turning it into a single space on insertion. However,
4111 EM_REPLACESEL on WinXP returns the number of characters in the original
4112 string.
4115 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4116 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4117 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4118 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4119 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4120 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4121 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4123 /* Test the actual string */
4124 getText.cb = 1024;
4125 getText.codepage = CP_ACP;
4126 getText.flags = GT_DEFAULT;
4127 getText.lpDefaultChar = NULL;
4128 getText.lpUsedDefChar = NULL;
4129 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4130 ok(strcmp(buffer, "\r\r") == 0,
4131 "EM_GETTEXTEX returned incorrect string\n");
4133 /* Test number of lines reported after EM_REPLACESEL */
4134 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4135 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4137 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4138 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4139 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4140 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4141 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4142 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4143 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4144 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4146 /* Test the actual string */
4147 getText.cb = 1024;
4148 getText.codepage = CP_ACP;
4149 getText.flags = GT_DEFAULT;
4150 getText.lpDefaultChar = NULL;
4151 getText.lpUsedDefChar = NULL;
4152 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4153 ok(strcmp(buffer, " ") == 0,
4154 "EM_GETTEXTEX returned incorrect string\n");
4156 /* Test number of lines reported after EM_REPLACESEL */
4157 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4158 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4160 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4161 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4162 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4163 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4164 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4165 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4166 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4167 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4169 /* Test the actual string */
4170 getText.cb = 1024;
4171 getText.codepage = CP_ACP;
4172 getText.flags = GT_DEFAULT;
4173 getText.lpDefaultChar = NULL;
4174 getText.lpUsedDefChar = NULL;
4175 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4176 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4177 "EM_GETTEXTEX returned incorrect string\n");
4179 /* Test number of lines reported after EM_REPLACESEL */
4180 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4181 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4183 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4184 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4185 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4186 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4187 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4188 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4189 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4190 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4192 /* Test the actual string */
4193 getText.cb = 1024;
4194 getText.codepage = CP_ACP;
4195 getText.flags = GT_DEFAULT;
4196 getText.lpDefaultChar = NULL;
4197 getText.lpUsedDefChar = NULL;
4198 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4199 ok(strcmp(buffer, " \r") == 0,
4200 "EM_GETTEXTEX returned incorrect string\n");
4202 /* Test number of lines reported after EM_REPLACESEL */
4203 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4204 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4206 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4207 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4208 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4209 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4210 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4211 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4212 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4213 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4215 /* Test the actual string */
4216 getText.cb = 1024;
4217 getText.codepage = CP_ACP;
4218 getText.flags = GT_DEFAULT;
4219 getText.lpDefaultChar = NULL;
4220 getText.lpUsedDefChar = NULL;
4221 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4222 ok(strcmp(buffer, " \r\r") == 0,
4223 "EM_GETTEXTEX returned incorrect string\n");
4225 /* Test number of lines reported after EM_REPLACESEL */
4226 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4227 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4229 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4230 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4231 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4232 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4233 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4234 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4235 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4236 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4238 /* Test the actual string */
4239 getText.cb = 1024;
4240 getText.codepage = CP_ACP;
4241 getText.flags = GT_DEFAULT;
4242 getText.lpDefaultChar = NULL;
4243 getText.lpUsedDefChar = NULL;
4244 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4245 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4246 "EM_GETTEXTEX returned incorrect string\n");
4248 /* Test number of lines reported after EM_REPLACESEL */
4249 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4250 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4252 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4253 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4254 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4255 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4256 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4257 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4258 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4260 /* Test the actual string */
4261 getText.cb = 1024;
4262 getText.codepage = CP_ACP;
4263 getText.flags = GT_DEFAULT;
4264 getText.lpDefaultChar = NULL;
4265 getText.lpUsedDefChar = NULL;
4266 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4267 ok(strcmp(buffer, "\r\r") == 0,
4268 "EM_GETTEXTEX returned incorrect string\n");
4270 /* Test number of lines reported after EM_REPLACESEL */
4271 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4272 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4274 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4275 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4276 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4277 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4278 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4279 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4280 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4281 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4283 /* Test the actual string */
4284 getText.cb = 1024;
4285 getText.codepage = CP_ACP;
4286 getText.flags = GT_DEFAULT;
4287 getText.lpDefaultChar = NULL;
4288 getText.lpUsedDefChar = NULL;
4289 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4290 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4291 "EM_GETTEXTEX returned incorrect string\n");
4293 /* Test number of lines reported after EM_REPLACESEL */
4294 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4295 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4297 if (!redraw)
4298 /* This is needed to avoid interferring with keybd_event calls
4299 * on other tests that simulate keyboard events. */
4300 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4302 DestroyWindow(hwndRichEdit);
4305 static void test_WM_PASTE(void)
4307 int result;
4308 char buffer[1024] = {0};
4309 const char* text1 = "testing paste\r";
4310 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4311 const char* text1_after = "testing paste\r\n";
4312 const char* text2 = "testing paste\r\rtesting paste";
4313 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4314 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4315 HWND hwndRichEdit = new_richedit(NULL);
4317 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
4318 messages, probably because it inspects the keyboard state itself.
4319 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
4322 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4323 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4324 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4325 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4326 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4328 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4329 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4331 SEND_CTRL_C(hwndRichEdit); /* Copy */
4332 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4333 SEND_CTRL_V(hwndRichEdit); /* Paste */
4334 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4335 /* Pasted text should be visible at this step */
4336 result = strcmp(text1_step1, buffer);
4337 ok(result == 0,
4338 "test paste: strcmp = %i\n", result);
4339 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4340 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4341 /* Text should be the same as before (except for \r -> \r\n conversion) */
4342 result = strcmp(text1_after, buffer);
4343 ok(result == 0,
4344 "test paste: strcmp = %i\n", result);
4346 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4347 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4348 SEND_CTRL_C(hwndRichEdit); /* Copy */
4349 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4350 SEND_CTRL_V(hwndRichEdit); /* Paste */
4351 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4352 /* Pasted text should be visible at this step */
4353 result = strcmp(text3, buffer);
4354 ok(result == 0,
4355 "test paste: strcmp = %i\n", result);
4356 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4357 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4358 /* Text should be the same as before (except for \r -> \r\n conversion) */
4359 result = strcmp(text2_after, buffer);
4360 ok(result == 0,
4361 "test paste: strcmp = %i\n", result);
4362 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4363 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4364 /* Text should revert to post-paste state */
4365 result = strcmp(buffer,text3);
4366 ok(result == 0,
4367 "test paste: strcmp = %i\n", result);
4369 DestroyWindow(hwndRichEdit);
4372 static void test_EM_FORMATRANGE(void)
4374 int r;
4375 FORMATRANGE fr;
4376 HDC hdc;
4377 HWND hwndRichEdit = new_richedit(NULL);
4379 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4381 hdc = GetDC(hwndRichEdit);
4382 ok(hdc != NULL, "Could not get HDC\n");
4384 fr.hdc = fr.hdcTarget = hdc;
4385 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4386 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4387 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4388 fr.chrg.cpMin = 0;
4389 fr.chrg.cpMax = 20;
4391 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4392 todo_wine {
4393 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4396 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4397 todo_wine {
4398 ok(r == 20 || r == 9, "EM_FORMATRANGE expect 20 or 9, got %d\n", r);
4401 fr.chrg.cpMin = 0;
4402 fr.chrg.cpMax = 10;
4404 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4405 todo_wine {
4406 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4409 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4410 todo_wine {
4411 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4414 DestroyWindow(hwndRichEdit);
4417 static int nCallbackCount = 0;
4419 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4420 LONG cb, LONG* pcb)
4422 const char text[] = {'t','e','s','t'};
4424 if (sizeof(text) <= cb)
4426 if ((int)dwCookie != nCallbackCount)
4428 *pcb = 0;
4429 return 0;
4432 memcpy (pbBuff, text, sizeof(text));
4433 *pcb = sizeof(text);
4435 nCallbackCount++;
4437 return 0;
4439 else
4440 return 1; /* indicates callback failed */
4443 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4444 LPBYTE pbBuff,
4445 LONG cb,
4446 LONG *pcb)
4448 const char** str = (const char**)dwCookie;
4449 int size = strlen(*str);
4450 *pcb = cb;
4451 if (*pcb > size) {
4452 *pcb = size;
4454 if (*pcb > 0) {
4455 memcpy(pbBuff, *str, *pcb);
4456 *str += *pcb;
4458 return 0;
4461 struct StringWithLength {
4462 int length;
4463 char *buffer;
4466 /* This callback is used to handled the null characters in a string. */
4467 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4468 LPBYTE pbBuff,
4469 LONG cb,
4470 LONG *pcb)
4472 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4473 int size = str->length;
4474 *pcb = cb;
4475 if (*pcb > size) {
4476 *pcb = size;
4478 if (*pcb > 0) {
4479 memcpy(pbBuff, str->buffer, *pcb);
4480 str->buffer += *pcb;
4481 str->length -= *pcb;
4483 return 0;
4486 static void test_EM_STREAMIN(void)
4488 HWND hwndRichEdit = new_richedit(NULL);
4489 LRESULT result;
4490 EDITSTREAM es;
4491 char buffer[1024] = {0};
4493 const char * streamText0 = "{\\rtf1 TestSomeText}";
4494 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4495 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4497 const char * streamText1 =
4498 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
4499 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
4500 "}\r\n";
4502 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4503 const char * streamText2 =
4504 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
4505 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
4506 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
4507 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
4508 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
4509 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
4510 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4512 const char * streamText3 = "RichEdit1";
4514 struct StringWithLength cookieForStream4;
4515 const char * streamText4 =
4516 "This text just needs to be long enough to cause run to be split onto "\
4517 "two separate lines and make sure the null terminating character is "\
4518 "handled properly.\0";
4519 int length4 = strlen(streamText4) + 1;
4520 cookieForStream4.buffer = (char *)streamText4;
4521 cookieForStream4.length = length4;
4523 /* Minimal test without \par at the end */
4524 es.dwCookie = (DWORD_PTR)&streamText0;
4525 es.dwError = 0;
4526 es.pfnCallback = test_EM_STREAMIN_esCallback;
4527 SendMessage(hwndRichEdit, EM_STREAMIN,
4528 (WPARAM)(SF_RTF), (LPARAM)&es);
4530 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4531 ok (result == 12,
4532 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4533 result = strcmp (buffer,"TestSomeText");
4534 ok (result == 0,
4535 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4536 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4538 /* Native richedit 2.0 ignores last \par */
4539 es.dwCookie = (DWORD_PTR)&streamText0a;
4540 es.dwError = 0;
4541 es.pfnCallback = test_EM_STREAMIN_esCallback;
4542 SendMessage(hwndRichEdit, EM_STREAMIN,
4543 (WPARAM)(SF_RTF), (LPARAM)&es);
4545 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4546 ok (result == 12,
4547 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4548 result = strcmp (buffer,"TestSomeText");
4549 ok (result == 0,
4550 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4551 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4553 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4554 es.dwCookie = (DWORD_PTR)&streamText0b;
4555 es.dwError = 0;
4556 es.pfnCallback = test_EM_STREAMIN_esCallback;
4557 SendMessage(hwndRichEdit, EM_STREAMIN,
4558 (WPARAM)(SF_RTF), (LPARAM)&es);
4560 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4561 ok (result == 14,
4562 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4563 result = strcmp (buffer,"TestSomeText\r\n");
4564 ok (result == 0,
4565 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4566 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4568 es.dwCookie = (DWORD_PTR)&streamText1;
4569 es.dwError = 0;
4570 es.pfnCallback = test_EM_STREAMIN_esCallback;
4571 SendMessage(hwndRichEdit, EM_STREAMIN,
4572 (WPARAM)(SF_RTF), (LPARAM)&es);
4574 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4575 ok (result == 12,
4576 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4577 result = strcmp (buffer,"TestSomeText");
4578 ok (result == 0,
4579 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4580 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4582 es.dwCookie = (DWORD_PTR)&streamText2;
4583 es.dwError = 0;
4584 SendMessage(hwndRichEdit, EM_STREAMIN,
4585 (WPARAM)(SF_RTF), (LPARAM)&es);
4587 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4588 ok (result == 0,
4589 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4590 ok (strlen(buffer) == 0,
4591 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4592 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4594 es.dwCookie = (DWORD_PTR)&streamText3;
4595 es.dwError = 0;
4596 SendMessage(hwndRichEdit, EM_STREAMIN,
4597 (WPARAM)(SF_RTF), (LPARAM)&es);
4599 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4600 ok (result == 0,
4601 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4602 ok (strlen(buffer) == 0,
4603 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4604 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4606 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4607 es.dwError = 0;
4608 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4609 SendMessage(hwndRichEdit, EM_STREAMIN,
4610 (WPARAM)(SF_TEXT), (LPARAM)&es);
4612 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4613 ok (result == length4,
4614 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4615 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4617 DestroyWindow(hwndRichEdit);
4620 static void test_EM_StreamIn_Undo(void)
4622 /* The purpose of this test is to determine when a EM_StreamIn should be
4623 * undoable. This is important because WM_PASTE currently uses StreamIn and
4624 * pasting should always be undoable but streaming isn't always.
4626 * cases to test:
4627 * StreamIn plain text without SFF_SELECTION.
4628 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4629 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4630 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4631 * Feel free to add tests for other text modes or StreamIn things.
4635 HWND hwndRichEdit = new_richedit(NULL);
4636 LRESULT result;
4637 EDITSTREAM es;
4638 char buffer[1024] = {0};
4639 const char randomtext[] = "Some text";
4641 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4643 /* StreamIn, no SFF_SELECTION */
4644 es.dwCookie = nCallbackCount;
4645 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4646 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4647 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4648 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4649 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4650 result = strcmp (buffer,"test");
4651 ok (result == 0,
4652 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4654 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4655 ok (result == FALSE,
4656 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4658 /* StreamIn, SFF_SELECTION, but nothing selected */
4659 es.dwCookie = nCallbackCount;
4660 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4661 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4662 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4663 SendMessage(hwndRichEdit, EM_STREAMIN,
4664 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4665 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4666 result = strcmp (buffer,"testSome text");
4667 ok (result == 0,
4668 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4670 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4671 ok (result == TRUE,
4672 "EM_STREAMIN with SFF_SELECTION but no selection set "
4673 "should create an undo\n");
4675 /* StreamIn, SFF_SELECTION, with a selection */
4676 es.dwCookie = nCallbackCount;
4677 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4678 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4679 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
4680 SendMessage(hwndRichEdit, EM_STREAMIN,
4681 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4682 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4683 result = strcmp (buffer,"Sometesttext");
4684 ok (result == 0,
4685 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4687 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4688 ok (result == TRUE,
4689 "EM_STREAMIN with SFF_SELECTION and selection set "
4690 "should create an undo\n");
4692 DestroyWindow(hwndRichEdit);
4695 static BOOL is_em_settextex_supported(HWND hwnd)
4697 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
4698 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
4701 static void test_unicode_conversions(void)
4703 static const WCHAR tW[] = {'t',0};
4704 static const WCHAR teW[] = {'t','e',0};
4705 static const WCHAR textW[] = {'t','e','s','t',0};
4706 static const char textA[] = "test";
4707 char bufA[64];
4708 WCHAR bufW[64];
4709 HWND hwnd;
4710 int is_win9x, em_settextex_supported, ret;
4712 is_win9x = GetVersion() & 0x80000000;
4714 #define set_textA(hwnd, wm_set_text, txt) \
4715 do { \
4716 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
4717 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4718 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4719 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4720 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
4721 } while(0)
4722 #define expect_textA(hwnd, wm_get_text, txt) \
4723 do { \
4724 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4725 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4726 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4727 memset(bufA, 0xAA, sizeof(bufA)); \
4728 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4729 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4730 ret = lstrcmpA(bufA, txt); \
4731 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
4732 } while(0)
4734 #define set_textW(hwnd, wm_set_text, txt) \
4735 do { \
4736 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
4737 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4738 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4739 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4740 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
4741 } while(0)
4742 #define expect_textW(hwnd, wm_get_text, txt) \
4743 do { \
4744 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
4745 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4746 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4747 memset(bufW, 0xAA, sizeof(bufW)); \
4748 if (is_win9x) \
4750 assert(wm_get_text == EM_GETTEXTEX); \
4751 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4752 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4754 else \
4756 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4757 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
4759 ret = lstrcmpW(bufW, txt); \
4760 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
4761 } while(0)
4762 #define expect_empty(hwnd, wm_get_text) \
4763 do { \
4764 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4765 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4766 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4767 memset(bufA, 0xAA, sizeof(bufA)); \
4768 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4769 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
4770 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
4771 } while(0)
4773 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4774 0, 0, 200, 60, 0, 0, 0, 0);
4775 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4777 ret = IsWindowUnicode(hwnd);
4778 if (is_win9x)
4779 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
4780 else
4781 ok(ret, "RichEdit20W should be unicode under NT\n");
4783 /* EM_SETTEXTEX is supported starting from version 3.0 */
4784 em_settextex_supported = is_em_settextex_supported(hwnd);
4785 trace("EM_SETTEXTEX is %ssupported on this platform\n",
4786 em_settextex_supported ? "" : "NOT ");
4788 expect_empty(hwnd, WM_GETTEXT);
4789 expect_empty(hwnd, EM_GETTEXTEX);
4791 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
4792 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4793 expect_textA(hwnd, WM_GETTEXT, "t");
4794 expect_textA(hwnd, EM_GETTEXTEX, "t");
4795 expect_textW(hwnd, EM_GETTEXTEX, tW);
4797 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
4798 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4799 expect_textA(hwnd, WM_GETTEXT, "te");
4800 expect_textA(hwnd, EM_GETTEXTEX, "te");
4801 expect_textW(hwnd, EM_GETTEXTEX, teW);
4803 set_textA(hwnd, WM_SETTEXT, NULL);
4804 expect_empty(hwnd, WM_GETTEXT);
4805 expect_empty(hwnd, EM_GETTEXTEX);
4807 if (is_win9x)
4808 set_textA(hwnd, WM_SETTEXT, textW);
4809 else
4810 set_textA(hwnd, WM_SETTEXT, textA);
4811 expect_textA(hwnd, WM_GETTEXT, textA);
4812 expect_textA(hwnd, EM_GETTEXTEX, textA);
4813 expect_textW(hwnd, EM_GETTEXTEX, textW);
4815 if (em_settextex_supported)
4817 set_textA(hwnd, EM_SETTEXTEX, textA);
4818 expect_textA(hwnd, WM_GETTEXT, textA);
4819 expect_textA(hwnd, EM_GETTEXTEX, textA);
4820 expect_textW(hwnd, EM_GETTEXTEX, textW);
4823 if (!is_win9x)
4825 set_textW(hwnd, WM_SETTEXT, textW);
4826 expect_textW(hwnd, WM_GETTEXT, textW);
4827 expect_textA(hwnd, WM_GETTEXT, textA);
4828 expect_textW(hwnd, EM_GETTEXTEX, textW);
4829 expect_textA(hwnd, EM_GETTEXTEX, textA);
4831 if (em_settextex_supported)
4833 set_textW(hwnd, EM_SETTEXTEX, textW);
4834 expect_textW(hwnd, WM_GETTEXT, textW);
4835 expect_textA(hwnd, WM_GETTEXT, textA);
4836 expect_textW(hwnd, EM_GETTEXTEX, textW);
4837 expect_textA(hwnd, EM_GETTEXTEX, textA);
4840 DestroyWindow(hwnd);
4842 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
4843 0, 0, 200, 60, 0, 0, 0, 0);
4844 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4846 ret = IsWindowUnicode(hwnd);
4847 ok(!ret, "RichEdit20A should NOT be unicode\n");
4849 set_textA(hwnd, WM_SETTEXT, textA);
4850 expect_textA(hwnd, WM_GETTEXT, textA);
4851 expect_textA(hwnd, EM_GETTEXTEX, textA);
4852 expect_textW(hwnd, EM_GETTEXTEX, textW);
4854 if (em_settextex_supported)
4856 set_textA(hwnd, EM_SETTEXTEX, textA);
4857 expect_textA(hwnd, WM_GETTEXT, textA);
4858 expect_textA(hwnd, EM_GETTEXTEX, textA);
4859 expect_textW(hwnd, EM_GETTEXTEX, textW);
4862 if (!is_win9x)
4864 set_textW(hwnd, WM_SETTEXT, textW);
4865 expect_textW(hwnd, WM_GETTEXT, textW);
4866 expect_textA(hwnd, WM_GETTEXT, textA);
4867 expect_textW(hwnd, EM_GETTEXTEX, textW);
4868 expect_textA(hwnd, EM_GETTEXTEX, textA);
4870 if (em_settextex_supported)
4872 set_textW(hwnd, EM_SETTEXTEX, textW);
4873 expect_textW(hwnd, WM_GETTEXT, textW);
4874 expect_textA(hwnd, WM_GETTEXT, textA);
4875 expect_textW(hwnd, EM_GETTEXTEX, textW);
4876 expect_textA(hwnd, EM_GETTEXTEX, textA);
4879 DestroyWindow(hwnd);
4882 static void test_WM_CHAR(void)
4884 HWND hwnd;
4885 int ret;
4886 const char * char_list = "abc\rabc\r";
4887 const char * expected_content_single = "abcabc";
4888 const char * expected_content_multi = "abc\r\nabc\r\n";
4889 char buffer[64] = {0};
4890 const char * p;
4892 /* single-line control must IGNORE carriage returns */
4893 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4894 0, 0, 200, 60, 0, 0, 0, 0);
4895 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4897 p = char_list;
4898 while (*p != '\0') {
4899 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4900 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4901 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4902 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4903 p++;
4906 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4907 ret = strcmp(buffer, expected_content_single);
4908 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4910 DestroyWindow(hwnd);
4912 /* multi-line control inserts CR normally */
4913 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4914 0, 0, 200, 60, 0, 0, 0, 0);
4915 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4917 p = char_list;
4918 while (*p != '\0') {
4919 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4920 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4921 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4922 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4923 p++;
4926 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4927 ret = strcmp(buffer, expected_content_multi);
4928 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4930 DestroyWindow(hwnd);
4933 static void test_EM_GETTEXTLENGTHEX(void)
4935 HWND hwnd;
4936 GETTEXTLENGTHEX gtl;
4937 int ret;
4938 const char * base_string = "base string";
4939 const char * test_string = "a\nb\n\n\r\n";
4940 const char * test_string_after = "a";
4941 const char * test_string_2 = "a\rtest\rstring";
4942 char buffer[64] = {0};
4944 /* single line */
4945 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4946 0, 0, 200, 60, 0, 0, 0, 0);
4947 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4949 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4950 gtl.codepage = CP_ACP;
4951 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4952 ok(ret == 0, "ret %d\n",ret);
4954 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4955 gtl.codepage = CP_ACP;
4956 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4957 ok(ret == 0, "ret %d\n",ret);
4959 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4961 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4962 gtl.codepage = CP_ACP;
4963 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4964 ok(ret == strlen(base_string), "ret %d\n",ret);
4966 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4967 gtl.codepage = CP_ACP;
4968 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4969 ok(ret == strlen(base_string), "ret %d\n",ret);
4971 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4973 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4974 gtl.codepage = CP_ACP;
4975 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4976 ok(ret == 1, "ret %d\n",ret);
4978 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4979 gtl.codepage = CP_ACP;
4980 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4981 ok(ret == 1, "ret %d\n",ret);
4983 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4984 ret = strcmp(buffer, test_string_after);
4985 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4987 DestroyWindow(hwnd);
4989 /* multi line */
4990 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
4991 0, 0, 200, 60, 0, 0, 0, 0);
4992 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4994 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4995 gtl.codepage = CP_ACP;
4996 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4997 ok(ret == 0, "ret %d\n",ret);
4999 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5000 gtl.codepage = CP_ACP;
5001 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5002 ok(ret == 0, "ret %d\n",ret);
5004 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5006 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5007 gtl.codepage = CP_ACP;
5008 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5009 ok(ret == strlen(base_string), "ret %d\n",ret);
5011 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5012 gtl.codepage = CP_ACP;
5013 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5014 ok(ret == strlen(base_string), "ret %d\n",ret);
5016 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5018 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5019 gtl.codepage = CP_ACP;
5020 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5021 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5023 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5024 gtl.codepage = CP_ACP;
5025 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5026 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5028 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5030 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5031 gtl.codepage = CP_ACP;
5032 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5033 ok(ret == 10, "ret %d\n",ret);
5035 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5036 gtl.codepage = CP_ACP;
5037 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5038 ok(ret == 6, "ret %d\n",ret);
5040 DestroyWindow(hwnd);
5044 /* globals that parent and child access when checking event masks & notifications */
5045 static HWND eventMaskEditHwnd = 0;
5046 static int queriedEventMask;
5047 static int watchForEventMask = 0;
5049 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5050 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5052 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5054 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5056 return DefWindowProcA(hwnd, message, wParam, lParam);
5059 /* test event masks in combination with WM_COMMAND */
5060 static void test_eventMask(void)
5062 HWND parent;
5063 int ret;
5064 WNDCLASSA cls;
5065 const char text[] = "foo bar\n";
5066 int eventMask;
5068 /* register class to capture WM_COMMAND */
5069 cls.style = 0;
5070 cls.lpfnWndProc = ParentMsgCheckProcA;
5071 cls.cbClsExtra = 0;
5072 cls.cbWndExtra = 0;
5073 cls.hInstance = GetModuleHandleA(0);
5074 cls.hIcon = 0;
5075 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5076 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5077 cls.lpszMenuName = NULL;
5078 cls.lpszClassName = "EventMaskParentClass";
5079 if(!RegisterClassA(&cls)) assert(0);
5081 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5082 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5083 ok (parent != 0, "Failed to create parent window\n");
5085 eventMaskEditHwnd = new_richedit(parent);
5086 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5088 eventMask = ENM_CHANGE | ENM_UPDATE;
5089 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5090 ok(ret == ENM_NONE, "wrong event mask\n");
5091 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5092 ok(ret == eventMask, "failed to set event mask\n");
5094 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5095 queriedEventMask = 0; /* initialize to something other than we expect */
5096 watchForEventMask = EN_CHANGE;
5097 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5098 ok(ret == TRUE, "failed to set text\n");
5099 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5100 notification in response to WM_SETTEXT */
5101 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5102 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5106 static int received_WM_NOTIFY = 0;
5107 static int modify_at_WM_NOTIFY = 0;
5108 static HWND hwndRichedit_WM_NOTIFY;
5110 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5112 if(message == WM_NOTIFY)
5114 received_WM_NOTIFY = 1;
5115 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5117 return DefWindowProcA(hwnd, message, wParam, lParam);
5120 static void test_WM_NOTIFY(void)
5122 HWND parent;
5123 WNDCLASSA cls;
5124 CHARFORMAT2 cf2;
5126 /* register class to capture WM_NOTIFY */
5127 cls.style = 0;
5128 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5129 cls.cbClsExtra = 0;
5130 cls.cbWndExtra = 0;
5131 cls.hInstance = GetModuleHandleA(0);
5132 cls.hIcon = 0;
5133 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5134 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5135 cls.lpszMenuName = NULL;
5136 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5137 if(!RegisterClassA(&cls)) assert(0);
5139 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5140 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5141 ok (parent != 0, "Failed to create parent window\n");
5143 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5144 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5146 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5148 /* Notifications for selection change should only be sent when selection
5149 actually changes. EM_SETCHARFORMAT is one message that calls
5150 ME_CommitUndo, which should check whether message should be sent */
5151 received_WM_NOTIFY = 0;
5152 cf2.cbSize = sizeof(CHARFORMAT2);
5153 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5154 (LPARAM) &cf2);
5155 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5156 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5157 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5158 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5160 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5161 already at 0. */
5162 received_WM_NOTIFY = 0;
5163 modify_at_WM_NOTIFY = 0;
5164 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5165 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5166 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5168 received_WM_NOTIFY = 0;
5169 modify_at_WM_NOTIFY = 0;
5170 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5171 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5173 received_WM_NOTIFY = 0;
5174 modify_at_WM_NOTIFY = 0;
5175 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5176 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5177 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5179 DestroyWindow(hwndRichedit_WM_NOTIFY);
5180 DestroyWindow(parent);
5183 static void test_undo_coalescing(void)
5185 HWND hwnd;
5186 int result;
5187 char buffer[64] = {0};
5189 /* multi-line control inserts CR normally */
5190 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5191 0, 0, 200, 60, 0, 0, 0, 0);
5192 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5194 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5195 ok (result == FALSE, "Can undo after window creation.\n");
5196 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5197 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5198 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5199 ok (result == FALSE, "Can redo after window creation.\n");
5200 result = SendMessage(hwnd, EM_REDO, 0, 0);
5201 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5203 /* Test the effect of arrows keys during typing on undo transactions*/
5204 simulate_typing_characters(hwnd, "one two three");
5205 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5206 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5207 simulate_typing_characters(hwnd, " four five six");
5209 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5210 ok (result == FALSE, "Can redo before anything is undone.\n");
5211 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5212 ok (result == TRUE, "Cannot undo typed characters.\n");
5213 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5214 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5215 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5216 ok (result == TRUE, "Cannot redo after undo.\n");
5217 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5218 result = strcmp(buffer, "one two three");
5219 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5221 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5222 ok (result == TRUE, "Cannot undo typed characters.\n");
5223 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5224 ok (result == TRUE, "Failed to undo typed characters.\n");
5225 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5226 result = strcmp(buffer, "");
5227 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5229 /* Test the effect of focus changes during typing on undo transactions*/
5230 simulate_typing_characters(hwnd, "one two three");
5231 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5232 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5233 SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
5234 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
5235 simulate_typing_characters(hwnd, " four five six");
5236 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5237 ok (result == TRUE, "Failed to undo typed characters.\n");
5238 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5239 result = strcmp(buffer, "one two three");
5240 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5242 /* Test the effect of the back key during typing on undo transactions */
5243 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5244 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5245 ok (result == TRUE, "Failed to clear the text.\n");
5246 simulate_typing_characters(hwnd, "one two threa");
5247 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5248 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5249 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5250 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5251 simulate_typing_characters(hwnd, "e four five six");
5252 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5253 ok (result == TRUE, "Failed to undo typed characters.\n");
5254 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5255 result = strcmp(buffer, "");
5256 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5258 /* Test the effect of the delete key during typing on undo transactions */
5259 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5260 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5261 ok(result == TRUE, "Failed to set the text.\n");
5262 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5263 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5264 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5265 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5266 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5267 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5268 ok (result == TRUE, "Failed to undo typed characters.\n");
5269 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5270 result = strcmp(buffer, "acd");
5271 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5272 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5273 ok (result == TRUE, "Failed to undo typed characters.\n");
5274 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5275 result = strcmp(buffer, "abcd");
5276 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5278 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5279 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5280 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5281 ok (result == TRUE, "Failed to clear the text.\n");
5282 simulate_typing_characters(hwnd, "one two three");
5283 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5284 ok (result == 0, "expected %d but got %d\n", 0, result);
5285 simulate_typing_characters(hwnd, " four five six");
5286 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5287 ok (result == TRUE, "Failed to undo typed characters.\n");
5288 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5289 result = strcmp(buffer, "one two three");
5290 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5291 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5292 ok (result == TRUE, "Failed to undo typed characters.\n");
5293 ok (result == TRUE, "Failed to undo typed characters.\n");
5294 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5295 result = strcmp(buffer, "");
5296 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5298 DestroyWindow(hwnd);
5301 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5302 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5304 static void test_word_movement(void)
5306 HWND hwnd;
5307 int result;
5308 int sel_start, sel_end;
5310 /* multi-line control inserts CR normally */
5311 hwnd = new_richedit(NULL);
5313 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5314 ok (result == TRUE, "Failed to clear the text.\n");
5315 SendMessage(hwnd, EM_SETSEL, 0, 0);
5316 /* |one two three */
5318 SEND_CTRL_RIGHT(hwnd);
5319 /* one |two three */
5320 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5321 ok(sel_start == sel_end, "Selection should be empty\n");
5322 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5324 SEND_CTRL_RIGHT(hwnd);
5325 /* one two |three */
5326 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5327 ok(sel_start == sel_end, "Selection should be empty\n");
5328 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5330 SEND_CTRL_LEFT(hwnd);
5331 /* one |two three */
5332 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5333 ok(sel_start == sel_end, "Selection should be empty\n");
5334 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5336 SEND_CTRL_LEFT(hwnd);
5337 /* |one two three */
5338 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5339 ok(sel_start == sel_end, "Selection should be empty\n");
5340 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5342 SendMessage(hwnd, EM_SETSEL, 8, 8);
5343 /* one two | three */
5344 SEND_CTRL_RIGHT(hwnd);
5345 /* one two |three */
5346 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5347 ok(sel_start == sel_end, "Selection should be empty\n");
5348 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5350 SendMessage(hwnd, EM_SETSEL, 11, 11);
5351 /* one two th|ree */
5352 SEND_CTRL_LEFT(hwnd);
5353 /* one two |three */
5354 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5355 ok(sel_start == sel_end, "Selection should be empty\n");
5356 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5358 DestroyWindow(hwnd);
5361 static void test_EM_CHARFROMPOS(void)
5363 HWND hwnd;
5364 int result;
5365 POINTL point;
5366 point.x = 0;
5367 point.y = 50;
5369 /* multi-line control inserts CR normally */
5370 hwnd = new_richedit(NULL);
5371 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5372 (LPARAM)"one two three four five six seven");
5374 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5375 ok(result == 0, "expected character index of 0 but got %d\n", result);
5377 DestroyWindow(hwnd);
5380 START_TEST( editor )
5382 MSG msg;
5383 time_t end;
5385 /* Must explicitly LoadLibrary(). The test has no references to functions in
5386 * RICHED20.DLL, so the linker doesn't actually link to it. */
5387 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
5388 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
5389 test_WM_CHAR();
5390 test_EM_FINDTEXT();
5391 test_EM_GETLINE();
5392 test_EM_POSFROMCHAR();
5393 test_EM_SCROLLCARET();
5394 test_EM_SCROLL();
5395 test_scrollbar_visibility();
5396 test_WM_SETTEXT();
5397 test_EM_LINELENGTH();
5398 test_EM_SETCHARFORMAT();
5399 test_EM_SETTEXTMODE();
5400 test_TM_PLAINTEXT();
5401 test_EM_SETOPTIONS();
5402 test_WM_GETTEXT();
5403 test_EM_GETTEXTRANGE();
5404 test_EM_GETSELTEXT();
5405 test_EM_SETUNDOLIMIT();
5406 test_ES_PASSWORD();
5407 test_EM_SETTEXTEX();
5408 test_EM_LIMITTEXT();
5409 test_EM_EXLIMITTEXT();
5410 test_EM_GETLIMITTEXT();
5411 test_WM_SETFONT();
5412 test_EM_GETMODIFY();
5413 test_EM_EXSETSEL();
5414 test_WM_PASTE();
5415 test_EM_STREAMIN();
5416 test_EM_STREAMOUT();
5417 test_EM_StreamIn_Undo();
5418 test_EM_FORMATRANGE();
5419 test_unicode_conversions();
5420 test_EM_GETTEXTLENGTHEX();
5421 test_EM_REPLACESEL(1);
5422 test_EM_REPLACESEL(0);
5423 test_WM_NOTIFY();
5424 test_EM_AUTOURLDETECT();
5425 test_eventMask();
5426 test_undo_coalescing();
5427 test_word_movement();
5428 test_EM_CHARFROMPOS();
5429 test_SETPARAFORMAT();
5431 /* Set the environment variable WINETEST_RICHED20 to keep windows
5432 * responsive and open for 30 seconds. This is useful for debugging.
5434 * The message pump uses PeekMessage() to empty the queue and then sleeps for
5435 * 50ms before retrying the queue. */
5436 end = time(NULL) + 30;
5437 if (getenv( "WINETEST_RICHED20" )) {
5438 while (time(NULL) < end) {
5439 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
5440 TranslateMessage(&msg);
5441 DispatchMessage(&msg);
5442 } else {
5443 Sleep(50);
5448 OleFlushClipboard();
5449 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());