push 615e57025c5810454991e0c38bb21161daed7f1f
[wine/hacks.git] / dlls / riched20 / tests / editor.c
blob8274721a8b29c398ad41c52619c3233c9dda217d
1 /*
2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
36 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
38 #define ok_w3(format, szString1, szString2, szString3) \
39 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
40 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
41 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
42 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
43 format, string1, string2, string3);
45 static HMODULE hmoduleRichEdit;
47 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
48 HWND hwnd;
49 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
50 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
51 hmoduleRichEdit, NULL);
52 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
53 return hwnd;
56 static HWND new_richedit(HWND parent) {
57 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
60 static void processPendingMessages(void)
62 MSG msg;
63 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
64 TranslateMessage(&msg);
65 DispatchMessage(&msg);
69 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
71 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
72 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
73 SetFocus(hwnd);
74 keybd_event(mod_vk, mod_scan_code, 0, 0);
75 keybd_event(vk, scan_code, 0, 0);
76 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
77 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
78 processPendingMessages();
81 static void simulate_typing_characters(HWND hwnd, const char* szChars)
83 int ret;
85 while (*szChars != '\0') {
86 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
87 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
88 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
89 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
90 szChars++;
94 static const char haystack[] = "WINEWine wineWine wine WineWine";
95 /* ^0 ^10 ^20 ^30 */
97 struct find_s {
98 int start;
99 int end;
100 const char *needle;
101 int flags;
102 int expected_loc;
103 int _todo_wine;
107 struct find_s find_tests[] = {
108 /* Find in empty text */
109 {0, -1, "foo", FR_DOWN, -1, 0},
110 {0, -1, "foo", 0, -1, 0},
111 {0, -1, "", FR_DOWN, -1, 0},
112 {20, 5, "foo", FR_DOWN, -1, 0},
113 {5, 20, "foo", FR_DOWN, -1, 0}
116 struct find_s find_tests2[] = {
117 /* No-result find */
118 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
119 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
121 /* Subsequent finds */
122 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
123 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
124 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
125 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
127 /* Find backwards */
128 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
129 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
130 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
132 /* Case-insensitive */
133 {1, 31, "wInE", FR_DOWN, 4, 0},
134 {1, 31, "Wine", FR_DOWN, 4, 0},
136 /* High-to-low ranges */
137 {20, 5, "Wine", FR_DOWN, -1, 0},
138 {2, 1, "Wine", FR_DOWN, -1, 0},
139 {30, 29, "Wine", FR_DOWN, -1, 0},
140 {20, 5, "Wine", 0, 13, 0},
142 /* Find nothing */
143 {5, 10, "", FR_DOWN, -1, 0},
144 {10, 5, "", FR_DOWN, -1, 0},
145 {0, -1, "", FR_DOWN, -1, 0},
146 {10, 5, "", 0, -1, 0},
148 /* Whole-word search */
149 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
150 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
151 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
152 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
153 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
154 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
155 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
157 /* Bad ranges */
158 {5, 200, "XXX", FR_DOWN, -1, 0},
159 {-20, 20, "Wine", FR_DOWN, -1, 0},
160 {-20, 20, "Wine", FR_DOWN, -1, 0},
161 {-15, -20, "Wine", FR_DOWN, -1, 0},
162 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
164 /* Check the case noted in bug 4479 where matches at end aren't recognized */
165 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
166 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
167 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
168 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
169 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
171 /* The backwards case of bug 4479; bounds look right
172 * Fails because backward find is wrong */
173 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
174 {0, 20, "WINE", FR_MATCHCASE, -1, 0},
176 {0, -1, "wineWine wine", 0, -1, 0},
179 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
180 int findloc;
181 FINDTEXT ft;
182 memset(&ft, 0, sizeof(ft));
183 ft.chrg.cpMin = f->start;
184 ft.chrg.cpMax = f->end;
185 ft.lpstrText = f->needle;
186 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
187 ok(findloc == f->expected_loc,
188 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
189 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
192 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
193 int id) {
194 int findloc;
195 FINDTEXTEX ft;
196 int expected_end_loc;
198 memset(&ft, 0, sizeof(ft));
199 ft.chrg.cpMin = f->start;
200 ft.chrg.cpMax = f->end;
201 ft.lpstrText = f->needle;
202 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
203 ok(findloc == f->expected_loc,
204 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
205 name, id, f->needle, f->start, f->end, f->flags, findloc);
206 ok(ft.chrgText.cpMin == f->expected_loc,
207 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
208 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
209 expected_end_loc = ((f->expected_loc == -1) ? -1
210 : f->expected_loc + strlen(f->needle));
211 ok(ft.chrgText.cpMax == expected_end_loc,
212 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
213 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
216 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
217 int num_tests)
219 int i;
221 for (i = 0; i < num_tests; i++) {
222 if (find[i]._todo_wine) {
223 todo_wine {
224 check_EM_FINDTEXT(hwnd, name, &find[i], i);
225 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
227 } else {
228 check_EM_FINDTEXT(hwnd, name, &find[i], i);
229 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
234 static void test_EM_FINDTEXT(void)
236 HWND hwndRichEdit = new_richedit(NULL);
237 CHARFORMAT2 cf2;
239 /* Empty rich edit control */
240 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
241 sizeof(find_tests)/sizeof(struct find_s));
243 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
245 /* Haystack text */
246 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
247 sizeof(find_tests2)/sizeof(struct find_s));
249 /* Setting a format on an arbitrary range should have no effect in search
250 results. This tests correct offset reporting across runs. */
251 cf2.cbSize = sizeof(CHARFORMAT2);
252 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
253 (LPARAM) &cf2);
254 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
255 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
256 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
257 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
259 /* Haystack text, again */
260 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
261 sizeof(find_tests2)/sizeof(struct find_s));
263 /* Yet another range */
264 cf2.dwMask = CFM_BOLD | cf2.dwMask;
265 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
266 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
267 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
269 /* Haystack text, again */
270 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
271 sizeof(find_tests2)/sizeof(struct find_s));
273 DestroyWindow(hwndRichEdit);
276 static const struct getline_s {
277 int line;
278 size_t buffer_len;
279 const char *text;
280 } gl[] = {
281 {0, 10, "foo bar\r"},
282 {1, 10, "\r"},
283 {2, 10, "bar\r"},
284 {3, 10, "\r"},
286 /* Buffer smaller than line length */
287 {0, 2, "foo bar\r"},
288 {0, 1, "foo bar\r"},
289 {0, 0, "foo bar\r"}
292 static void test_EM_GETLINE(void)
294 int i;
295 HWND hwndRichEdit = new_richedit(NULL);
296 static const int nBuf = 1024;
297 char dest[1024], origdest[1024];
298 const char text[] = "foo bar\n"
299 "\n"
300 "bar\n";
302 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
304 memset(origdest, 0xBB, nBuf);
305 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
307 int nCopied;
308 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
309 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
310 memset(dest, 0xBB, nBuf);
311 *(WORD *) dest = gl[i].buffer_len;
313 /* EM_GETLINE appends a "\r\0" to the end of the line
314 * nCopied counts up to and including the '\r' */
315 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
316 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
317 expected_nCopied);
318 /* two special cases since a parameter is passed via dest */
319 if (gl[i].buffer_len == 0)
320 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
321 "buffer_len=0\n");
322 else if (gl[i].buffer_len == 1)
323 ok(dest[0] == gl[i].text[0] && !dest[1] &&
324 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
325 else
327 /* Prepare hex strings of buffers to dump on failure. */
328 char expectedbuf[1024];
329 char resultbuf[1024];
330 int j;
331 expectedbuf[0] = '\0';
332 for (j = 0; j < 32; j++)
333 sprintf(expectedbuf+strlen(expectedbuf), "%02x", dest[j] & 0xFF);
334 resultbuf[0] = '\0';
335 for (j = 0; j < expected_bytes_written; j++)
336 sprintf(resultbuf+strlen(resultbuf), "%02x", gl[i].text[j] & 0xFF);
337 for (; j < 32; j++)
338 sprintf(resultbuf+strlen(resultbuf), "%02x", origdest[j] & 0xFF);
340 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
341 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
342 i, expected_bytes_written, expectedbuf, resultbuf);
343 ok(!strncmp(dest + expected_bytes_written, origdest
344 + expected_bytes_written, nBuf - expected_bytes_written),
345 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
346 i, expected_bytes_written, expectedbuf, resultbuf);
350 DestroyWindow(hwndRichEdit);
353 static void test_EM_LINELENGTH(void)
355 HWND hwndRichEdit = new_richedit(NULL);
356 const char * text =
357 "richedit1\r"
358 "richedit1\n"
359 "richedit1\r\n"
360 "richedit1";
361 int offset_test[10][2] = {
362 {0, 9},
363 {5, 9},
364 {10, 9},
365 {15, 9},
366 {20, 9},
367 {25, 9},
368 {30, 9},
369 {35, 9},
370 {40, 0},
371 {45, 0},
373 int i;
374 LRESULT result;
376 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
378 for (i = 0; i < 10; i++) {
379 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
380 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
381 offset_test[i][0], result, offset_test[i][1]);
384 DestroyWindow(hwndRichEdit);
387 static int get_scroll_pos_y(HWND hwnd)
389 POINT p = {-1, -1};
390 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
391 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
392 return p.y;
395 static void move_cursor(HWND hwnd, long charindex)
397 CHARRANGE cr;
398 cr.cpMax = charindex;
399 cr.cpMin = charindex;
400 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
403 static void line_scroll(HWND hwnd, int amount)
405 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
408 static void test_EM_SCROLLCARET(void)
410 int prevY, curY;
411 const char text[] = "aa\n"
412 "this is a long line of text that should be longer than the "
413 "control's width\n"
414 "cc\n"
415 "dd\n"
416 "ee\n"
417 "ff\n"
418 "gg\n"
419 "hh\n";
420 /* The richedit window height needs to be large enough vertically to fit in
421 * more than two lines of text, so the new_richedit function can't be used
422 * since a height of 60 was not large enough on some systems.
424 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
425 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
426 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
427 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
429 /* Can't verify this */
430 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
432 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
434 /* Caret above visible window */
435 line_scroll(hwndRichEdit, 3);
436 prevY = get_scroll_pos_y(hwndRichEdit);
437 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
438 curY = get_scroll_pos_y(hwndRichEdit);
439 ok(prevY != curY, "%d == %d\n", prevY, curY);
441 /* Caret below visible window */
442 move_cursor(hwndRichEdit, sizeof(text) - 1);
443 line_scroll(hwndRichEdit, -3);
444 prevY = get_scroll_pos_y(hwndRichEdit);
445 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
446 curY = get_scroll_pos_y(hwndRichEdit);
447 ok(prevY != curY, "%d == %d\n", prevY, curY);
449 /* Caret in visible window */
450 move_cursor(hwndRichEdit, sizeof(text) - 2);
451 prevY = get_scroll_pos_y(hwndRichEdit);
452 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
453 curY = get_scroll_pos_y(hwndRichEdit);
454 ok(prevY == curY, "%d != %d\n", prevY, curY);
456 /* Caret still in visible window */
457 line_scroll(hwndRichEdit, -1);
458 prevY = get_scroll_pos_y(hwndRichEdit);
459 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
460 curY = get_scroll_pos_y(hwndRichEdit);
461 ok(prevY == curY, "%d != %d\n", prevY, curY);
463 DestroyWindow(hwndRichEdit);
466 static void test_EM_POSFROMCHAR(void)
468 HWND hwndRichEdit = new_richedit(NULL);
469 int i;
470 LRESULT result;
471 unsigned int height = 0;
472 int xpos = 0;
473 static const char text[] = "aa\n"
474 "this is a long line of text that should be longer than the "
475 "control's width\n"
476 "cc\n"
477 "dd\n"
478 "ee\n"
479 "ff\n"
480 "gg\n"
481 "hh\n";
483 /* Fill the control to lines to ensure that most of them are offscreen */
484 for (i = 0; i < 50; i++)
486 /* Do not modify the string; it is exactly 16 characters long. */
487 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
488 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
492 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
493 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
494 Richedit 3.0 accepts either of the above API conventions.
497 /* Testing Richedit 2.0 API format */
499 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
500 Since all lines are identical and drawn with the same font,
501 they should have the same height... right?
503 for (i = 0; i < 50; i++)
505 /* All the lines are 16 characters long */
506 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
507 if (i == 0)
509 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
510 todo_wine {
511 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
513 xpos = LOWORD(result);
515 else if (i == 1)
517 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
518 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
519 height = HIWORD(result);
521 else
523 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
524 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
528 /* Testing position at end of text */
529 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
530 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
531 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
533 /* Testing position way past end of text */
534 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
535 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
536 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
538 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
539 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
540 for (i = 0; i < 50; i++)
542 /* All the lines are 16 characters long */
543 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
544 ok((signed short)(HIWORD(result)) == (i - 1) * height,
545 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
546 (signed short)(HIWORD(result)), (i - 1) * height);
547 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
550 /* Testing position at end of text */
551 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
552 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
553 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
555 /* Testing position way past end of text */
556 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
557 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
558 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
560 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
561 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
562 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
564 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
565 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
566 todo_wine {
567 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
569 xpos = LOWORD(result);
571 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
572 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
573 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
574 todo_wine {
575 /* Fails on builtin because horizontal scrollbar is not being shown */
576 ok((signed short)(LOWORD(result)) < xpos,
577 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
578 (signed short)(LOWORD(result)), xpos);
580 DestroyWindow(hwndRichEdit);
583 static void test_EM_SETCHARFORMAT(void)
585 HWND hwndRichEdit = new_richedit(NULL);
586 CHARFORMAT2 cf2;
587 int rc = 0;
588 int tested_effects[] = {
589 CFE_BOLD,
590 CFE_ITALIC,
591 CFE_UNDERLINE,
592 CFE_STRIKEOUT,
593 CFE_PROTECTED,
594 CFE_LINK,
595 CFE_SUBSCRIPT,
596 CFE_SUPERSCRIPT,
599 int i;
600 CHARRANGE cr;
602 /* Invalid flags, CHARFORMAT2 structure blanked out */
603 memset(&cf2, 0, sizeof(cf2));
604 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
605 (LPARAM) &cf2);
606 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
608 /* A valid flag, CHARFORMAT2 structure blanked out */
609 memset(&cf2, 0, sizeof(cf2));
610 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
611 (LPARAM) &cf2);
612 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
614 /* A valid flag, CHARFORMAT2 structure blanked out */
615 memset(&cf2, 0, sizeof(cf2));
616 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
617 (LPARAM) &cf2);
618 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
620 /* A valid flag, CHARFORMAT2 structure blanked out */
621 memset(&cf2, 0, sizeof(cf2));
622 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
623 (LPARAM) &cf2);
624 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
626 /* A valid flag, CHARFORMAT2 structure blanked out */
627 memset(&cf2, 0, sizeof(cf2));
628 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
629 (LPARAM) &cf2);
630 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
632 /* Invalid flags, CHARFORMAT2 structure minimally filled */
633 memset(&cf2, 0, sizeof(cf2));
634 cf2.cbSize = sizeof(CHARFORMAT2);
635 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
636 (LPARAM) &cf2);
637 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
638 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
639 ok(rc == FALSE, "Should not be able to undo here.\n");
640 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
642 /* A valid flag, CHARFORMAT2 structure minimally filled */
643 memset(&cf2, 0, sizeof(cf2));
644 cf2.cbSize = sizeof(CHARFORMAT2);
645 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
646 (LPARAM) &cf2);
647 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
648 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
649 ok(rc == FALSE, "Should not be able to undo here.\n");
650 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
652 /* A valid flag, CHARFORMAT2 structure minimally filled */
653 memset(&cf2, 0, sizeof(cf2));
654 cf2.cbSize = sizeof(CHARFORMAT2);
655 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
656 (LPARAM) &cf2);
657 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
658 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
659 ok(rc == FALSE, "Should not be able to undo here.\n");
660 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
662 /* A valid flag, CHARFORMAT2 structure minimally filled */
663 memset(&cf2, 0, sizeof(cf2));
664 cf2.cbSize = sizeof(CHARFORMAT2);
665 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
666 (LPARAM) &cf2);
667 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
668 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
669 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
670 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
672 /* A valid flag, CHARFORMAT2 structure minimally filled */
673 memset(&cf2, 0, sizeof(cf2));
674 cf2.cbSize = sizeof(CHARFORMAT2);
675 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
676 (LPARAM) &cf2);
677 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
678 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
679 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
680 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
682 cf2.cbSize = sizeof(CHARFORMAT2);
683 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
684 (LPARAM) &cf2);
686 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
687 cf2.cbSize = sizeof(CHARFORMAT2);
688 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
689 (LPARAM) &cf2);
690 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
691 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
693 /* wParam==0 is default char format, does not set modify */
694 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
695 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
696 ok(rc == 0, "Text marked as modified, expected not modified!\n");
697 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
698 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
699 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
700 ok(rc == 0, "Text marked as modified, expected not modified!\n");
702 /* wParam==SCF_SELECTION sets modify if nonempty selection */
703 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
704 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
705 ok(rc == 0, "Text marked as modified, expected not modified!\n");
706 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
707 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
708 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
709 ok(rc == 0, "Text marked as modified, expected not modified!\n");
711 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
712 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
713 ok(rc == 0, "Text marked as modified, expected not modified!\n");
714 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
715 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
716 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
717 ok(rc == 0, "Text marked as modified, expected not modified!\n");
718 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
719 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
720 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
721 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
722 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
724 /* wParam==SCF_ALL sets modify regardless of whether text is present */
725 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
726 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
727 ok(rc == 0, "Text marked as modified, expected not modified!\n");
728 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
729 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
730 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
731 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
733 DestroyWindow(hwndRichEdit);
735 /* EM_GETCHARFORMAT tests */
736 for (i = 0; tested_effects[i]; i++)
738 hwndRichEdit = new_richedit(NULL);
739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
741 /* Need to set a TrueType font to get consistent CFM_BOLD results */
742 memset(&cf2, 0, sizeof(CHARFORMAT2));
743 cf2.cbSize = sizeof(CHARFORMAT2);
744 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
745 cf2.dwEffects = 0;
746 strcpy(cf2.szFaceName, "Courier New");
747 cf2.wWeight = FW_DONTCARE;
748 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
750 memset(&cf2, 0, sizeof(CHARFORMAT2));
751 cf2.cbSize = sizeof(CHARFORMAT2);
752 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
753 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
754 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
755 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
757 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
758 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
759 ok((cf2.dwEffects & tested_effects[i]) == 0,
760 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
762 memset(&cf2, 0, sizeof(CHARFORMAT2));
763 cf2.cbSize = sizeof(CHARFORMAT2);
764 cf2.dwMask = tested_effects[i];
765 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
766 cf2.dwMask = CFM_SUPERSCRIPT;
767 cf2.dwEffects = tested_effects[i];
768 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
769 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
771 memset(&cf2, 0, sizeof(CHARFORMAT2));
772 cf2.cbSize = sizeof(CHARFORMAT2);
773 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
774 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
775 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
776 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
778 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
779 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
780 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
781 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
783 memset(&cf2, 0, sizeof(CHARFORMAT2));
784 cf2.cbSize = sizeof(CHARFORMAT2);
785 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
786 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
787 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
788 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
790 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
791 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
792 ok((cf2.dwEffects & tested_effects[i]) == 0,
793 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
795 memset(&cf2, 0, sizeof(CHARFORMAT2));
796 cf2.cbSize = sizeof(CHARFORMAT2);
797 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
798 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
799 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
800 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
802 (cf2.dwMask & tested_effects[i]) == 0),
803 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
805 DestroyWindow(hwndRichEdit);
808 for (i = 0; tested_effects[i]; i++)
810 hwndRichEdit = new_richedit(NULL);
811 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
813 /* Need to set a TrueType font to get consistent CFM_BOLD results */
814 memset(&cf2, 0, sizeof(CHARFORMAT2));
815 cf2.cbSize = sizeof(CHARFORMAT2);
816 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
817 cf2.dwEffects = 0;
818 strcpy(cf2.szFaceName, "Courier New");
819 cf2.wWeight = FW_DONTCARE;
820 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
822 memset(&cf2, 0, sizeof(CHARFORMAT2));
823 cf2.cbSize = sizeof(CHARFORMAT2);
824 cf2.dwMask = tested_effects[i];
825 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
826 cf2.dwMask = CFM_SUPERSCRIPT;
827 cf2.dwEffects = tested_effects[i];
828 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
829 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
831 memset(&cf2, 0, sizeof(CHARFORMAT2));
832 cf2.cbSize = sizeof(CHARFORMAT2);
833 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
834 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
835 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
836 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
838 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
839 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
840 ok((cf2.dwEffects & tested_effects[i]) == 0,
841 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
843 memset(&cf2, 0, sizeof(CHARFORMAT2));
844 cf2.cbSize = sizeof(CHARFORMAT2);
845 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
846 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
847 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
848 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
850 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
851 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
852 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
853 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
855 memset(&cf2, 0, sizeof(CHARFORMAT2));
856 cf2.cbSize = sizeof(CHARFORMAT2);
857 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
858 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
859 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
860 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
862 (cf2.dwMask & tested_effects[i]) == 0),
863 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
864 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
865 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
867 DestroyWindow(hwndRichEdit);
870 /* Effects applied on an empty selection should take effect when selection is
871 replaced with text */
872 hwndRichEdit = new_richedit(NULL);
873 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
874 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
876 memset(&cf2, 0, sizeof(CHARFORMAT2));
877 cf2.cbSize = sizeof(CHARFORMAT2);
878 cf2.dwMask = CFM_BOLD;
879 cf2.dwEffects = CFE_BOLD;
880 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
882 /* Selection is now nonempty */
883 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
885 memset(&cf2, 0, sizeof(CHARFORMAT2));
886 cf2.cbSize = sizeof(CHARFORMAT2);
887 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
888 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
890 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
891 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
892 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
893 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
896 /* Set two effects on an empty selection */
897 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
898 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
900 memset(&cf2, 0, sizeof(CHARFORMAT2));
901 cf2.cbSize = sizeof(CHARFORMAT2);
902 cf2.dwMask = CFM_BOLD;
903 cf2.dwEffects = CFE_BOLD;
904 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
905 cf2.dwMask = CFM_ITALIC;
906 cf2.dwEffects = CFE_ITALIC;
907 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
909 /* Selection is now nonempty */
910 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
912 memset(&cf2, 0, sizeof(CHARFORMAT2));
913 cf2.cbSize = sizeof(CHARFORMAT2);
914 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
915 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
917 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
918 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
919 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
920 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
922 /* Setting the (empty) selection to exactly the same place as before should
923 NOT clear the insertion style! */
924 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
925 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
927 memset(&cf2, 0, sizeof(CHARFORMAT2));
928 cf2.cbSize = sizeof(CHARFORMAT2);
929 cf2.dwMask = CFM_BOLD;
930 cf2.dwEffects = CFE_BOLD;
931 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
933 /* Empty selection in same place, insert style should NOT be forgotten here. */
934 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
936 /* Selection is now nonempty */
937 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
939 memset(&cf2, 0, sizeof(CHARFORMAT2));
940 cf2.cbSize = sizeof(CHARFORMAT2);
941 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
942 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
944 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
945 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
946 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
947 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
949 /* Ditto with EM_EXSETSEL */
950 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
951 cr.cpMin = 2; cr.cpMax = 2;
952 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
954 memset(&cf2, 0, sizeof(CHARFORMAT2));
955 cf2.cbSize = sizeof(CHARFORMAT2);
956 cf2.dwMask = CFM_BOLD;
957 cf2.dwEffects = CFE_BOLD;
958 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
960 /* Empty selection in same place, insert style should NOT be forgotten here. */
961 cr.cpMin = 2; cr.cpMax = 2;
962 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
964 /* Selection is now nonempty */
965 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
967 memset(&cf2, 0, sizeof(CHARFORMAT2));
968 cf2.cbSize = sizeof(CHARFORMAT2);
969 cr.cpMin = 2; cr.cpMax = 6;
970 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
971 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
973 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
974 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
975 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
976 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
978 DestroyWindow(hwndRichEdit);
981 static void test_EM_SETTEXTMODE(void)
983 HWND hwndRichEdit = new_richedit(NULL);
984 CHARFORMAT2 cf2, cf2test;
985 CHARRANGE cr;
986 int rc = 0;
988 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
989 /*Insert text into the control*/
991 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
993 /*Attempt to change the control to plain text mode*/
994 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
995 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
997 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
998 If rich text is pasted, it should have the same formatting as the rest
999 of the text in the control*/
1001 /*Italicize the text
1002 *NOTE: If the default text was already italicized, the test will simply
1003 reverse; in other words, it will copy a regular "wine" into a plain
1004 text window that uses an italicized format*/
1005 cf2.cbSize = sizeof(CHARFORMAT2);
1006 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1007 (LPARAM) &cf2);
1009 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1010 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1012 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1013 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1015 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1016 however, SCF_ALL has been implemented*/
1017 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1018 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1020 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1021 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1023 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1025 /*Select the string "wine"*/
1026 cr.cpMin = 0;
1027 cr.cpMax = 4;
1028 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1030 /*Copy the italicized "wine" to the clipboard*/
1031 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1033 /*Reset the formatting to default*/
1034 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1035 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1036 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1038 /*Clear the text in the control*/
1039 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1041 /*Switch to Plain Text Mode*/
1042 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1043 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1045 /*Input "wine" again in normal format*/
1046 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1048 /*Paste the italicized "wine" into the control*/
1049 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1051 /*Select a character from the first "wine" string*/
1052 cr.cpMin = 2;
1053 cr.cpMax = 3;
1054 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1056 /*Retrieve its formatting*/
1057 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1058 (LPARAM) &cf2);
1060 /*Select a character from the second "wine" string*/
1061 cr.cpMin = 5;
1062 cr.cpMax = 6;
1063 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1065 /*Retrieve its formatting*/
1066 cf2test.cbSize = sizeof(CHARFORMAT2);
1067 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1068 (LPARAM) &cf2test);
1070 /*Compare the two formattings*/
1071 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1072 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1073 cf2.dwEffects, cf2test.dwEffects);
1074 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1075 printing "wine" in the current format(normal)
1076 pasting "wine" from the clipboard(italicized)
1077 comparing the two formats(should differ)*/
1079 /*Attempt to switch with text in control*/
1080 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1081 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1083 /*Clear control*/
1084 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1086 /*Switch into Rich Text mode*/
1087 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1088 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1090 /*Print "wine" in normal formatting into the control*/
1091 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1093 /*Paste italicized "wine" into the control*/
1094 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1096 /*Select text from the first "wine" string*/
1097 cr.cpMin = 1;
1098 cr.cpMax = 3;
1099 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1101 /*Retrieve its formatting*/
1102 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1103 (LPARAM) &cf2);
1105 /*Select text from the second "wine" string*/
1106 cr.cpMin = 6;
1107 cr.cpMax = 7;
1108 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1110 /*Retrieve its formatting*/
1111 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1112 (LPARAM) &cf2test);
1114 /*Test that the two formattings are not the same*/
1115 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1116 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1117 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1119 DestroyWindow(hwndRichEdit);
1122 static void test_SETPARAFORMAT(void)
1124 HWND hwndRichEdit = new_richedit(NULL);
1125 PARAFORMAT2 fmt;
1126 HRESULT ret;
1127 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1128 fmt.cbSize = sizeof(PARAFORMAT2);
1129 fmt.dwMask = PFM_ALIGNMENT;
1130 fmt.wAlignment = PFA_LEFT;
1132 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1133 ok(ret != 0, "expected non-zero got %d\n", ret);
1135 fmt.cbSize = sizeof(PARAFORMAT2);
1136 fmt.dwMask = -1;
1137 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1138 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1139 * between richedit different native builds of riched20.dll
1140 * used on different Windows versions. */
1141 ret &= ~PFM_TABLEROWDELIMITER;
1142 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1144 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1145 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1147 DestroyWindow(hwndRichEdit);
1150 static void test_TM_PLAINTEXT(void)
1152 /*Tests plain text properties*/
1154 HWND hwndRichEdit = new_richedit(NULL);
1155 CHARFORMAT2 cf2, cf2test;
1156 CHARRANGE cr;
1157 int rc = 0;
1159 /*Switch to plain text mode*/
1161 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1162 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1164 /*Fill control with text*/
1166 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1168 /*Select some text and bold it*/
1170 cr.cpMin = 10;
1171 cr.cpMax = 20;
1172 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1173 cf2.cbSize = sizeof(CHARFORMAT2);
1174 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1175 (LPARAM) &cf2);
1177 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1178 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1180 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1181 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1183 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1184 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1186 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1187 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1189 /*Get the formatting of those characters*/
1191 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1193 /*Get the formatting of some other characters*/
1194 cf2test.cbSize = sizeof(CHARFORMAT2);
1195 cr.cpMin = 21;
1196 cr.cpMax = 30;
1197 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1198 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1200 /*Test that they are the same as plain text allows only one formatting*/
1202 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1203 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1204 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1206 /*Fill the control with a "wine" string, which when inserted will be bold*/
1208 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1210 /*Copy the bolded "wine" string*/
1212 cr.cpMin = 0;
1213 cr.cpMax = 4;
1214 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1215 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1217 /*Swap back to rich text*/
1219 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1220 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1222 /*Set the default formatting to bold italics*/
1224 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1225 cf2.dwMask |= CFM_ITALIC;
1226 cf2.dwEffects ^= CFE_ITALIC;
1227 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1228 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1230 /*Set the text in the control to "wine", which will be bold and italicized*/
1232 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1234 /*Paste the plain text "wine" string, which should take the insert
1235 formatting, which at the moment is bold italics*/
1237 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1239 /*Select the first "wine" string and retrieve its formatting*/
1241 cr.cpMin = 1;
1242 cr.cpMax = 3;
1243 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1244 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1246 /*Select the second "wine" string and retrieve its formatting*/
1248 cr.cpMin = 5;
1249 cr.cpMax = 7;
1250 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1251 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1253 /*Compare the two formattings. They should be the same.*/
1255 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1256 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1257 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1258 DestroyWindow(hwndRichEdit);
1261 static void test_WM_GETTEXT(void)
1263 HWND hwndRichEdit = new_richedit(NULL);
1264 static const char text[] = "Hello. My name is RichEdit!";
1265 static const char text2[] = "Hello. My name is RichEdit!\r";
1266 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1267 char buffer[1024] = {0};
1268 int result;
1270 /* Baseline test with normal-sized buffer */
1271 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1272 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1273 ok(result == lstrlen(buffer),
1274 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1275 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1276 result = strcmp(buffer,text);
1277 ok(result == 0,
1278 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1280 /* Test for returned value of WM_GETTEXTLENGTH */
1281 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1282 ok(result == lstrlen(text),
1283 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1284 result, lstrlen(text));
1286 /* Test for behavior in overflow case */
1287 memset(buffer, 0, 1024);
1288 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1289 ok(result == 0 ||
1290 result == lstrlenA(text) - 1, /* XP, win2k3 */
1291 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1292 result = strcmp(buffer,text);
1293 if (result)
1294 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1295 ok(result == 0,
1296 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1298 /* Baseline test with normal-sized buffer and carriage return */
1299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1300 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1301 ok(result == lstrlen(buffer),
1302 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1303 result = strcmp(buffer,text2_after);
1304 ok(result == 0,
1305 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1307 /* Test for returned value of WM_GETTEXTLENGTH */
1308 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1309 ok(result == lstrlen(text2_after),
1310 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1311 result, lstrlen(text2_after));
1313 /* Test for behavior of CRLF conversion in case of overflow */
1314 memset(buffer, 0, 1024);
1315 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1316 ok(result == 0 ||
1317 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1318 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1319 result = strcmp(buffer,text2);
1320 if (result)
1321 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1322 ok(result == 0,
1323 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1325 DestroyWindow(hwndRichEdit);
1328 static void test_EM_GETTEXTRANGE(void)
1330 HWND hwndRichEdit = new_richedit(NULL);
1331 const char * text1 = "foo bar\r\nfoo bar";
1332 const char * text2 = "foo bar\rfoo bar";
1333 const char * expect = "bar\rfoo";
1334 char buffer[1024] = {0};
1335 LRESULT result;
1336 TEXTRANGEA textRange;
1338 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1340 textRange.lpstrText = buffer;
1341 textRange.chrg.cpMin = 4;
1342 textRange.chrg.cpMax = 11;
1343 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1344 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1345 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1347 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1349 textRange.lpstrText = buffer;
1350 textRange.chrg.cpMin = 4;
1351 textRange.chrg.cpMax = 11;
1352 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1353 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1354 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1356 DestroyWindow(hwndRichEdit);
1359 static void test_EM_GETSELTEXT(void)
1361 HWND hwndRichEdit = new_richedit(NULL);
1362 const char * text1 = "foo bar\r\nfoo bar";
1363 const char * text2 = "foo bar\rfoo bar";
1364 const char * expect = "bar\rfoo";
1365 char buffer[1024] = {0};
1366 LRESULT result;
1368 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1370 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1371 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1372 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1373 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1375 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1377 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1378 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1379 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1380 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1382 DestroyWindow(hwndRichEdit);
1385 /* FIXME: need to test unimplemented options and robustly test wparam */
1386 static void test_EM_SETOPTIONS(void)
1388 HWND hwndRichEdit = new_richedit(NULL);
1389 static const char text[] = "Hello. My name is RichEdit!";
1390 char buffer[1024] = {0};
1392 /* NEGATIVE TESTING - NO OPTIONS SET */
1393 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1394 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1396 /* testing no readonly by sending 'a' to the control*/
1397 SetFocus(hwndRichEdit);
1398 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1399 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1400 ok(buffer[0]=='a',
1401 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1402 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1404 /* READONLY - sending 'a' to the control */
1405 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1406 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1407 SetFocus(hwndRichEdit);
1408 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1409 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1410 ok(buffer[0]==text[0],
1411 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1413 DestroyWindow(hwndRichEdit);
1416 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1418 CHARFORMAT2W text_format;
1419 text_format.cbSize = sizeof(text_format);
1420 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1421 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1422 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1425 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1427 int link_present = 0;
1429 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1430 if (is_url)
1431 { /* control text is url; should get CFE_LINK */
1432 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1434 else
1436 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1440 static HWND new_static_wnd(HWND parent) {
1441 return new_window("Static", 0, parent);
1444 static void test_EM_AUTOURLDETECT(void)
1446 /* DO NOT change the properties of the first two elements. To shorten the
1447 tests, all tests after WM_SETTEXT test just the first two elements -
1448 one non-URL and one URL */
1449 struct urls_s {
1450 const char *text;
1451 int is_url;
1452 } urls[12] = {
1453 {"winehq.org", 0},
1454 {"http://www.winehq.org", 1},
1455 {"http//winehq.org", 0},
1456 {"ww.winehq.org", 0},
1457 {"www.winehq.org", 1},
1458 {"ftp://192.168.1.1", 1},
1459 {"ftp//192.168.1.1", 0},
1460 {"mailto:your@email.com", 1},
1461 {"prospero:prosperoserver", 1},
1462 {"telnet:test", 1},
1463 {"news:newserver", 1},
1464 {"wais:waisserver", 1}
1467 int i, j;
1468 int urlRet=-1;
1469 HWND hwndRichEdit, parent;
1471 /* All of the following should cause the URL to be detected */
1472 const char * templates_delim[] = {
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\r 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",
1482 "This text ends with X",
1484 "This is some text with X) on it",
1485 "This is some text with X--- on it",
1486 "This is some text with X\" on it",
1487 "This is some text with X' on it",
1488 "This is some text with X: on it",
1490 "This is some text with (X on it",
1491 "This is some text with \rX 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",
1497 /* None of these should cause the URL to be detected */
1498 const char * templates_non_delim[] = {
1499 "This is some text with |X| on it",
1500 "This is some text with *X* on it",
1501 "This is some text with /X/ on it",
1502 "This is some text with +X+ on it",
1503 "This is some text with %X% on it",
1504 "This is some text with #X# on it",
1505 "This is some text with @X@ on it",
1506 "This is some text with \\X\\ on it",
1507 "This is some text with |X on it",
1508 "This is some text with *X on it",
1509 "This is some text with /X on it",
1510 "This is some text with +X on it",
1511 "This is some text with %X on it",
1512 "This is some text with #X on it",
1513 "This is some text with @X on it",
1514 "This is some text with \\X on it",
1516 /* All of these cause the URL detection to be extended by one more byte,
1517 thus demonstrating that the tested character is considered as part
1518 of the URL. */
1519 const char * templates_xten_delim[] = {
1520 "This is some text with X| on it",
1521 "This is some text with X* on it",
1522 "This is some text with X/ on it",
1523 "This is some text with X+ on it",
1524 "This is some text with X% on it",
1525 "This is some text with X# on it",
1526 "This is some text with X@ on it",
1527 "This is some text with X\\ on it",
1529 char buffer[1024];
1531 parent = new_static_wnd(NULL);
1532 hwndRichEdit = new_richedit(parent);
1533 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1534 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1535 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1536 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1537 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1538 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1539 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1540 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1541 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1542 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1543 /* for each url, check the text to see if CFE_LINK effect is present */
1544 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1546 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1547 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1548 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1550 /* Link detection should happen immediately upon WM_SETTEXT */
1551 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1552 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1553 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1555 DestroyWindow(hwndRichEdit);
1557 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1558 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1559 hwndRichEdit = new_richedit(parent);
1561 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1562 char * at_pos;
1563 int at_offset;
1564 int end_offset;
1566 at_pos = strchr(templates_delim[j], 'X');
1567 at_offset = at_pos - templates_delim[j];
1568 strncpy(buffer, templates_delim[j], at_offset);
1569 buffer[at_offset] = '\0';
1570 strcat(buffer, urls[i].text);
1571 strcat(buffer, templates_delim[j] + at_offset + 1);
1572 end_offset = at_offset + strlen(urls[i].text);
1574 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1575 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1577 /* This assumes no templates start with the URL itself, and that they
1578 have at least two characters before the URL text */
1579 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1580 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1581 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1582 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1583 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1584 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1586 if (urls[i].is_url)
1588 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1589 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1590 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1591 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1593 else
1595 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1596 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1597 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1598 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1600 if (buffer[end_offset] != '\0')
1602 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1603 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1604 if (buffer[end_offset +1] != '\0')
1606 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1607 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1612 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1613 char * at_pos;
1614 int at_offset;
1615 int end_offset;
1617 at_pos = strchr(templates_non_delim[j], 'X');
1618 at_offset = at_pos - templates_non_delim[j];
1619 strncpy(buffer, templates_non_delim[j], at_offset);
1620 buffer[at_offset] = '\0';
1621 strcat(buffer, urls[i].text);
1622 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1623 end_offset = at_offset + strlen(urls[i].text);
1625 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1626 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1628 /* This assumes no templates start with the URL itself, and that they
1629 have at least two characters before the URL text */
1630 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1631 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1632 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1633 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1634 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1635 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1637 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1638 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1639 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1640 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1641 if (buffer[end_offset] != '\0')
1643 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1644 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1645 if (buffer[end_offset +1] != '\0')
1647 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1648 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1653 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1654 char * at_pos;
1655 int at_offset;
1656 int end_offset;
1658 at_pos = strchr(templates_xten_delim[j], 'X');
1659 at_offset = at_pos - templates_xten_delim[j];
1660 strncpy(buffer, templates_xten_delim[j], at_offset);
1661 buffer[at_offset] = '\0';
1662 strcat(buffer, urls[i].text);
1663 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1664 end_offset = at_offset + strlen(urls[i].text);
1666 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1667 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1669 /* This assumes no templates start with the URL itself, and that they
1670 have at least two characters before the URL text */
1671 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1672 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1673 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1674 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1675 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1676 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1678 if (urls[i].is_url)
1680 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1681 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1682 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1683 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1684 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1685 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1687 else
1689 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1690 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1691 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1692 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1693 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1694 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1696 if (buffer[end_offset +1] != '\0')
1698 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1699 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1700 if (buffer[end_offset +2] != '\0')
1702 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1703 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1708 DestroyWindow(hwndRichEdit);
1709 hwndRichEdit = NULL;
1712 /* Test detection of URLs within normal text - WM_CHAR case. */
1713 /* Test only the first two URL examples for brevity */
1714 for (i = 0; i < 2; i++) {
1715 hwndRichEdit = new_richedit(parent);
1717 /* Also for brevity, test only the first three delimiters */
1718 for (j = 0; j < 3; j++) {
1719 char * at_pos;
1720 int at_offset;
1721 int end_offset;
1722 int u, v;
1724 at_pos = strchr(templates_delim[j], 'X');
1725 at_offset = at_pos - templates_delim[j];
1726 end_offset = at_offset + strlen(urls[i].text);
1728 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1729 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1730 for (u = 0; templates_delim[j][u]; u++) {
1731 if (templates_delim[j][u] == '\r') {
1732 simulate_typing_characters(hwndRichEdit, "\r");
1733 } else if (templates_delim[j][u] != 'X') {
1734 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1735 } else {
1736 for (v = 0; urls[i].text[v]; v++) {
1737 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1741 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1743 /* This assumes no templates start with the URL itself, and that they
1744 have at least two characters before the URL text */
1745 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1746 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1747 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1748 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1749 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1750 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1752 if (urls[i].is_url)
1754 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1755 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1756 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1757 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1759 else
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 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1764 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1766 if (buffer[end_offset] != '\0')
1768 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1769 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1770 if (buffer[end_offset +1] != '\0')
1772 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1773 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1777 /* The following will insert a paragraph break after the first character
1778 of the URL candidate, thus breaking the URL. It is expected that the
1779 CFE_LINK attribute should break across both pieces of the URL */
1780 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1781 simulate_typing_characters(hwndRichEdit, "\r");
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 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1792 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1793 /* end_offset moved because of paragraph break */
1794 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1795 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1796 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1797 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1799 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1800 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1801 if (buffer[end_offset +2] != '\0')
1803 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1804 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1808 /* The following will remove the just-inserted paragraph break, thus
1809 restoring the URL */
1810 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1811 simulate_typing_characters(hwndRichEdit, "\b");
1812 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1814 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1815 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1816 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1817 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1818 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1819 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1821 if (urls[i].is_url)
1823 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1824 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1825 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1826 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1828 else
1830 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1831 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1832 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1833 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1835 if (buffer[end_offset] != '\0')
1837 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1838 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1839 if (buffer[end_offset +1] != '\0')
1841 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1842 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1846 DestroyWindow(hwndRichEdit);
1847 hwndRichEdit = NULL;
1850 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1851 /* Test just the first two URL examples for brevity */
1852 for (i = 0; i < 2; i++) {
1853 SETTEXTEX st;
1855 hwndRichEdit = new_richedit(parent);
1857 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1858 be detected:
1859 1) Set entire text, a la WM_SETTEXT
1860 2) Set a selection of the text to the URL
1861 3) Set a portion of the text at a time, which eventually results in
1862 an URL
1863 All of them should give equivalent results
1866 /* Set entire text in one go, like WM_SETTEXT */
1867 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1868 char * at_pos;
1869 int at_offset;
1870 int end_offset;
1872 st.codepage = CP_ACP;
1873 st.flags = ST_DEFAULT;
1875 at_pos = strchr(templates_delim[j], 'X');
1876 at_offset = at_pos - templates_delim[j];
1877 strncpy(buffer, templates_delim[j], at_offset);
1878 buffer[at_offset] = '\0';
1879 strcat(buffer, urls[i].text);
1880 strcat(buffer, templates_delim[j] + at_offset + 1);
1881 end_offset = at_offset + strlen(urls[i].text);
1883 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1884 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1886 /* This assumes no templates start with the URL itself, and that they
1887 have at least two characters before the URL text */
1888 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1889 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1890 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1891 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1892 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1893 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1895 if (urls[i].is_url)
1897 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1898 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1899 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1900 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1902 else
1904 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1905 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1906 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1907 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1909 if (buffer[end_offset] != '\0')
1911 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1912 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1913 if (buffer[end_offset +1] != '\0')
1915 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1916 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1921 /* Set selection with X to the URL */
1922 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1923 char * at_pos;
1924 int at_offset;
1925 int end_offset;
1927 at_pos = strchr(templates_delim[j], 'X');
1928 at_offset = at_pos - templates_delim[j];
1929 end_offset = at_offset + strlen(urls[i].text);
1931 st.codepage = CP_ACP;
1932 st.flags = ST_DEFAULT;
1933 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1934 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1935 st.flags = ST_SELECTION;
1936 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1937 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1938 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1940 /* This assumes no templates start with the URL itself, and that they
1941 have at least two characters before the URL text */
1942 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1943 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1944 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1945 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1946 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1947 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1949 if (urls[i].is_url)
1951 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1952 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1953 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1954 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1956 else
1958 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1959 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1960 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1961 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1963 if (buffer[end_offset] != '\0')
1965 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1966 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1967 if (buffer[end_offset +1] != '\0')
1969 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1970 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1975 /* Set selection with X to the first character of the URL, then the rest */
1976 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1977 char * at_pos;
1978 int at_offset;
1979 int end_offset;
1981 at_pos = strchr(templates_delim[j], 'X');
1982 at_offset = at_pos - templates_delim[j];
1983 end_offset = at_offset + strlen(urls[i].text);
1985 strcpy(buffer, "YY");
1986 buffer[0] = urls[i].text[0];
1988 st.codepage = CP_ACP;
1989 st.flags = ST_DEFAULT;
1990 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1991 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1992 st.flags = ST_SELECTION;
1993 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1994 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1995 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1996 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1997 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1999 /* This assumes no templates start with the URL itself, and that they
2000 have at least two characters before the URL text */
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2003 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2004 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2005 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2006 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2008 if (urls[i].is_url)
2010 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2011 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2012 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2013 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2015 else
2017 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2018 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2019 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2020 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2022 if (buffer[end_offset] != '\0')
2024 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2025 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2026 if (buffer[end_offset +1] != '\0')
2028 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2029 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2034 DestroyWindow(hwndRichEdit);
2035 hwndRichEdit = NULL;
2038 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2039 /* Test just the first two URL examples for brevity */
2040 for (i = 0; i < 2; i++) {
2041 hwndRichEdit = new_richedit(parent);
2043 /* Set selection with X to the URL */
2044 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2045 char * at_pos;
2046 int at_offset;
2047 int end_offset;
2049 at_pos = strchr(templates_delim[j], 'X');
2050 at_offset = at_pos - templates_delim[j];
2051 end_offset = at_offset + strlen(urls[i].text);
2053 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2054 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2055 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2056 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2057 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2059 /* This assumes no templates start with the URL itself, and that they
2060 have at least two characters before the URL text */
2061 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2062 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2063 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2064 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2065 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2066 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2068 if (urls[i].is_url)
2070 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2071 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2072 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2073 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2075 else
2077 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2078 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2079 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2080 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2082 if (buffer[end_offset] != '\0')
2084 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2085 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2086 if (buffer[end_offset +1] != '\0')
2088 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2089 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2094 /* Set selection with X to the first character of the URL, then the rest */
2095 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2096 char * at_pos;
2097 int at_offset;
2098 int end_offset;
2100 at_pos = strchr(templates_delim[j], 'X');
2101 at_offset = at_pos - templates_delim[j];
2102 end_offset = at_offset + strlen(urls[i].text);
2104 strcpy(buffer, "YY");
2105 buffer[0] = urls[i].text[0];
2107 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2108 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2109 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2110 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2111 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2112 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2113 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2115 /* This assumes no templates start with the URL itself, and that they
2116 have at least two characters before the URL text */
2117 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2118 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2119 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2120 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2121 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2122 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2124 if (urls[i].is_url)
2126 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2127 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2128 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2129 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2131 else
2133 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2134 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2135 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2136 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2138 if (buffer[end_offset] != '\0')
2140 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2141 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2142 if (buffer[end_offset +1] != '\0')
2144 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2145 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2150 DestroyWindow(hwndRichEdit);
2151 hwndRichEdit = NULL;
2154 DestroyWindow(parent);
2157 static void test_EM_SCROLL(void)
2159 int i, j;
2160 int r; /* return value */
2161 int expr; /* expected return value */
2162 HWND hwndRichEdit = new_richedit(NULL);
2163 int y_before, y_after; /* units of lines of text */
2165 /* test a richedit box containing a single line of text */
2166 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2167 expr = 0x00010000;
2168 for (i = 0; i < 4; i++) {
2169 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2171 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2172 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2173 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2174 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2175 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2176 "(i == %d)\n", y_after, i);
2180 * test a richedit box that will scroll. There are two general
2181 * cases: the case without any long lines and the case with a long
2182 * line.
2184 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2185 if (i == 0)
2186 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2187 else
2188 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2189 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2190 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2191 "LONG LINE \nb\nc\nd\ne");
2192 for (j = 0; j < 12; j++) /* reset scroll position to top */
2193 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2195 /* get first visible line */
2196 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2197 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2199 /* get new current first visible line */
2200 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2202 ok(((r & 0xffffff00) == 0x00010000) &&
2203 ((r & 0x000000ff) != 0x00000000),
2204 "EM_SCROLL page down didn't scroll by a small positive number of "
2205 "lines (r == 0x%08x)\n", r);
2206 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2207 "(line %d scrolled to line %d\n", y_before, y_after);
2209 y_before = y_after;
2211 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2212 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2213 ok(((r & 0xffffff00) == 0x0001ff00),
2214 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2215 "(r == 0x%08x)\n", r);
2216 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2217 "%d scrolled to line %d\n", y_before, y_after);
2219 y_before = y_after;
2221 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2223 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2225 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2226 "(r == 0x%08x)\n", r);
2227 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2228 "1 line (%d scrolled to %d)\n", y_before, y_after);
2230 y_before = y_after;
2232 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2234 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2236 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2237 "(r == 0x%08x)\n", r);
2238 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2239 "line (%d scrolled to %d)\n", y_before, y_after);
2241 y_before = y_after;
2243 r = SendMessage(hwndRichEdit, EM_SCROLL,
2244 SB_LINEUP, 0); /* lineup beyond top */
2246 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2248 ok(r == 0x00010000,
2249 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2250 ok(y_before == y_after,
2251 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2253 y_before = y_after;
2255 r = SendMessage(hwndRichEdit, EM_SCROLL,
2256 SB_PAGEUP, 0);/*page up beyond top */
2258 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2260 ok(r == 0x00010000,
2261 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2262 ok(y_before == y_after,
2263 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2265 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2266 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2267 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2268 r = SendMessage(hwndRichEdit, EM_SCROLL,
2269 SB_PAGEDOWN, 0); /* page down beyond bot */
2270 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2272 ok(r == 0x00010000,
2273 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2274 ok(y_before == y_after,
2275 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2276 y_before, y_after);
2278 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2279 SendMessage(hwndRichEdit, EM_SCROLL,
2280 SB_LINEDOWN, 0); /* line down beyond bot */
2281 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2283 ok(r == 0x00010000,
2284 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2285 ok(y_before == y_after,
2286 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2287 y_before, y_after);
2289 DestroyWindow(hwndRichEdit);
2292 unsigned int recursionLevel = 0;
2293 unsigned int WM_SIZE_recursionLevel = 0;
2294 BOOL bailedOutOfRecursion = FALSE;
2295 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2297 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2299 LRESULT r;
2301 if (bailedOutOfRecursion) return 0;
2302 if (recursionLevel >= 32) {
2303 bailedOutOfRecursion = TRUE;
2304 return 0;
2307 recursionLevel++;
2308 switch (message) {
2309 case WM_SIZE:
2310 WM_SIZE_recursionLevel++;
2311 r = richeditProc(hwnd, message, wParam, lParam);
2312 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2313 ShowScrollBar(hwnd, SB_VERT, TRUE);
2314 WM_SIZE_recursionLevel--;
2315 break;
2316 default:
2317 r = richeditProc(hwnd, message, wParam, lParam);
2318 break;
2320 recursionLevel--;
2321 return r;
2324 static void test_scrollbar_visibility(void)
2326 HWND hwndRichEdit;
2327 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2328 SCROLLINFO si;
2329 WNDCLASSA cls;
2330 BOOL r;
2332 /* These tests show that richedit should temporarily refrain from automatically
2333 hiding or showing its scrollbars (vertical at least) when an explicit request
2334 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2335 Some applications depend on forced showing (when otherwise richedit would
2336 hide the vertical scrollbar) and are thrown on an endless recursive loop
2337 if richedit auto-hides the scrollbar again. Apparently they never heard of
2338 the ES_DISABLENOSCROLL style... */
2340 hwndRichEdit = new_richedit(NULL);
2342 /* Test default scrollbar visibility behavior */
2343 memset(&si, 0, sizeof(si));
2344 si.cbSize = sizeof(si);
2345 si.fMask = SIF_PAGE | SIF_RANGE;
2346 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2347 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2348 "Vertical scrollbar is visible, should be invisible.\n");
2349 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2350 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2351 si.nPage, si.nMin, si.nMax);
2353 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2354 memset(&si, 0, sizeof(si));
2355 si.cbSize = sizeof(si);
2356 si.fMask = SIF_PAGE | SIF_RANGE;
2357 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2358 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2359 "Vertical scrollbar is visible, should be invisible.\n");
2360 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2361 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2362 si.nPage, si.nMin, si.nMax);
2364 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2365 memset(&si, 0, sizeof(si));
2366 si.cbSize = sizeof(si);
2367 si.fMask = SIF_PAGE | SIF_RANGE;
2368 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2369 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2370 "Vertical scrollbar is invisible, should be visible.\n");
2371 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2372 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2373 si.nPage, si.nMin, si.nMax);
2375 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2376 even though it hides the scrollbar */
2377 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2378 memset(&si, 0, sizeof(si));
2379 si.cbSize = sizeof(si);
2380 si.fMask = SIF_PAGE | SIF_RANGE;
2381 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2382 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2383 "Vertical scrollbar is visible, should be invisible.\n");
2384 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2385 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2386 si.nPage, si.nMin, si.nMax);
2388 /* Setting non-scrolling text again does *not* reset scrollbar range */
2389 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2390 memset(&si, 0, sizeof(si));
2391 si.cbSize = sizeof(si);
2392 si.fMask = SIF_PAGE | SIF_RANGE;
2393 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2394 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2395 "Vertical scrollbar is visible, should be invisible.\n");
2396 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2397 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2398 si.nPage, si.nMin, si.nMax);
2400 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2401 memset(&si, 0, sizeof(si));
2402 si.cbSize = sizeof(si);
2403 si.fMask = SIF_PAGE | SIF_RANGE;
2404 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2405 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2406 "Vertical scrollbar is visible, should be invisible.\n");
2407 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2408 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2409 si.nPage, si.nMin, si.nMax);
2411 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2412 memset(&si, 0, sizeof(si));
2413 si.cbSize = sizeof(si);
2414 si.fMask = SIF_PAGE | SIF_RANGE;
2415 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2416 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2417 "Vertical scrollbar is visible, should be invisible.\n");
2418 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2419 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2420 si.nPage, si.nMin, si.nMax);
2422 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2423 memset(&si, 0, sizeof(si));
2424 si.cbSize = sizeof(si);
2425 si.fMask = SIF_PAGE | SIF_RANGE;
2426 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2427 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2428 "Vertical scrollbar is visible, should be invisible.\n");
2429 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2430 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2431 si.nPage, si.nMin, si.nMax);
2433 DestroyWindow(hwndRichEdit);
2435 /* Test again, with ES_DISABLENOSCROLL style */
2436 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2438 /* Test default scrollbar visibility behavior */
2439 memset(&si, 0, sizeof(si));
2440 si.cbSize = sizeof(si);
2441 si.fMask = SIF_PAGE | SIF_RANGE;
2442 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2443 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2444 "Vertical scrollbar is invisible, should be visible.\n");
2445 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2446 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2447 si.nPage, si.nMin, si.nMax);
2449 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2450 memset(&si, 0, sizeof(si));
2451 si.cbSize = sizeof(si);
2452 si.fMask = SIF_PAGE | SIF_RANGE;
2453 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2454 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2455 "Vertical scrollbar is invisible, should be visible.\n");
2456 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2457 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2458 si.nPage, si.nMin, si.nMax);
2460 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2461 memset(&si, 0, sizeof(si));
2462 si.cbSize = sizeof(si);
2463 si.fMask = SIF_PAGE | SIF_RANGE;
2464 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2465 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2466 "Vertical scrollbar is invisible, should be visible.\n");
2467 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2468 "reported page/range is %d (%d..%d)\n",
2469 si.nPage, si.nMin, si.nMax);
2471 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2472 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2473 memset(&si, 0, sizeof(si));
2474 si.cbSize = sizeof(si);
2475 si.fMask = SIF_PAGE | SIF_RANGE;
2476 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2477 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2478 "Vertical scrollbar is invisible, should be visible.\n");
2479 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2480 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2481 si.nPage, si.nMin, si.nMax);
2483 /* Setting non-scrolling text again does *not* reset scrollbar range */
2484 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2485 memset(&si, 0, sizeof(si));
2486 si.cbSize = sizeof(si);
2487 si.fMask = SIF_PAGE | SIF_RANGE;
2488 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2489 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2490 "Vertical scrollbar is invisible, should be visible.\n");
2491 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2492 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2493 si.nPage, si.nMin, si.nMax);
2495 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2496 memset(&si, 0, sizeof(si));
2497 si.cbSize = sizeof(si);
2498 si.fMask = SIF_PAGE | SIF_RANGE;
2499 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2500 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2501 "Vertical scrollbar is invisible, should be visible.\n");
2502 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2503 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2504 si.nPage, si.nMin, si.nMax);
2506 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2507 memset(&si, 0, sizeof(si));
2508 si.cbSize = sizeof(si);
2509 si.fMask = SIF_PAGE | SIF_RANGE;
2510 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2511 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2512 "Vertical scrollbar is invisible, should be visible.\n");
2513 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2514 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2515 si.nPage, si.nMin, si.nMax);
2517 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2518 memset(&si, 0, sizeof(si));
2519 si.cbSize = sizeof(si);
2520 si.fMask = SIF_PAGE | SIF_RANGE;
2521 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2522 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2523 "Vertical scrollbar is invisible, should be visible.\n");
2524 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2525 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2526 si.nPage, si.nMin, si.nMax);
2528 DestroyWindow(hwndRichEdit);
2530 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2531 hwndRichEdit = new_richedit(NULL);
2533 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2534 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2535 memset(&si, 0, sizeof(si));
2536 si.cbSize = sizeof(si);
2537 si.fMask = SIF_PAGE | SIF_RANGE;
2538 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2539 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2540 "Vertical scrollbar is invisible, should be visible.\n");
2541 todo_wine {
2542 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2543 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2544 si.nPage, si.nMin, si.nMax);
2547 /* Ditto, see above */
2548 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2549 memset(&si, 0, sizeof(si));
2550 si.cbSize = sizeof(si);
2551 si.fMask = SIF_PAGE | SIF_RANGE;
2552 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2553 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2554 "Vertical scrollbar is invisible, should be visible.\n");
2555 todo_wine {
2556 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2557 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2558 si.nPage, si.nMin, si.nMax);
2561 /* Ditto, see above */
2562 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2563 memset(&si, 0, sizeof(si));
2564 si.cbSize = sizeof(si);
2565 si.fMask = SIF_PAGE | SIF_RANGE;
2566 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2567 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2568 "Vertical scrollbar is invisible, should be visible.\n");
2569 todo_wine {
2570 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2571 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2572 si.nPage, si.nMin, si.nMax);
2575 /* Ditto, see above */
2576 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2577 memset(&si, 0, sizeof(si));
2578 si.cbSize = sizeof(si);
2579 si.fMask = SIF_PAGE | SIF_RANGE;
2580 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2581 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2582 "Vertical scrollbar is invisible, should be visible.\n");
2583 todo_wine {
2584 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2585 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2586 si.nPage, si.nMin, si.nMax);
2589 /* Ditto, see above */
2590 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2591 memset(&si, 0, sizeof(si));
2592 si.cbSize = sizeof(si);
2593 si.fMask = SIF_PAGE | SIF_RANGE;
2594 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2595 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2596 "Vertical scrollbar is invisible, should be visible.\n");
2597 todo_wine {
2598 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2599 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2600 si.nPage, si.nMin, si.nMax);
2603 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2604 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2605 memset(&si, 0, sizeof(si));
2606 si.cbSize = sizeof(si);
2607 si.fMask = SIF_PAGE | SIF_RANGE;
2608 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2609 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2610 "Vertical scrollbar is visible, should be invisible.\n");
2611 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2612 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2613 si.nPage, si.nMin, si.nMax);
2615 DestroyWindow(hwndRichEdit);
2617 hwndRichEdit = new_richedit(NULL);
2619 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2620 memset(&si, 0, sizeof(si));
2621 si.cbSize = sizeof(si);
2622 si.fMask = SIF_PAGE | SIF_RANGE;
2623 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2624 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2625 "Vertical scrollbar is visible, should be invisible.\n");
2626 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2627 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2628 si.nPage, si.nMin, si.nMax);
2630 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2631 memset(&si, 0, sizeof(si));
2632 si.cbSize = sizeof(si);
2633 si.fMask = SIF_PAGE | SIF_RANGE;
2634 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2635 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2636 "Vertical scrollbar is visible, should be invisible.\n");
2637 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2638 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2639 si.nPage, si.nMin, si.nMax);
2641 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2642 memset(&si, 0, sizeof(si));
2643 si.cbSize = sizeof(si);
2644 si.fMask = SIF_PAGE | SIF_RANGE;
2645 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2646 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2647 "Vertical scrollbar is visible, should be invisible.\n");
2648 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2649 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2650 si.nPage, si.nMin, si.nMax);
2652 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2653 memset(&si, 0, sizeof(si));
2654 si.cbSize = sizeof(si);
2655 si.fMask = SIF_PAGE | SIF_RANGE;
2656 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2657 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2658 "Vertical scrollbar is visible, should be invisible.\n");
2659 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2660 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2661 si.nPage, si.nMin, si.nMax);
2663 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2664 memset(&si, 0, sizeof(si));
2665 si.cbSize = sizeof(si);
2666 si.fMask = SIF_PAGE | SIF_RANGE;
2667 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2668 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2669 "Vertical scrollbar is invisible, should be visible.\n");
2670 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2671 "reported page/range is %d (%d..%d)\n",
2672 si.nPage, si.nMin, si.nMax);
2674 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2675 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2676 memset(&si, 0, sizeof(si));
2677 si.cbSize = sizeof(si);
2678 si.fMask = SIF_PAGE | SIF_RANGE;
2679 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2680 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2681 "Vertical scrollbar is visible, should be invisible.\n");
2682 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2683 "reported page/range is %d (%d..%d)\n",
2684 si.nPage, si.nMin, si.nMax);
2686 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2687 memset(&si, 0, sizeof(si));
2688 si.cbSize = sizeof(si);
2689 si.fMask = SIF_PAGE | SIF_RANGE;
2690 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2691 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2692 "Vertical scrollbar is visible, should be invisible.\n");
2693 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2694 "reported page/range is %d (%d..%d)\n",
2695 si.nPage, si.nMin, si.nMax);
2697 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2698 EM_SCROLL will make visible any forcefully invisible scrollbar */
2699 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2700 memset(&si, 0, sizeof(si));
2701 si.cbSize = sizeof(si);
2702 si.fMask = SIF_PAGE | SIF_RANGE;
2703 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2704 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2705 "Vertical scrollbar is invisible, should be visible.\n");
2706 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2707 "reported page/range is %d (%d..%d)\n",
2708 si.nPage, si.nMin, si.nMax);
2710 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2711 memset(&si, 0, sizeof(si));
2712 si.cbSize = sizeof(si);
2713 si.fMask = SIF_PAGE | SIF_RANGE;
2714 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2715 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2716 "Vertical scrollbar is visible, should be invisible.\n");
2717 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2718 "reported page/range is %d (%d..%d)\n",
2719 si.nPage, si.nMin, si.nMax);
2721 /* Again, EM_SCROLL, with SB_LINEUP */
2722 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2723 memset(&si, 0, sizeof(si));
2724 si.cbSize = sizeof(si);
2725 si.fMask = SIF_PAGE | SIF_RANGE;
2726 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2727 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2728 "Vertical scrollbar is invisible, should be visible.\n");
2729 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2730 "reported page/range is %d (%d..%d)\n",
2731 si.nPage, si.nMin, si.nMax);
2733 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2734 memset(&si, 0, sizeof(si));
2735 si.cbSize = sizeof(si);
2736 si.fMask = SIF_PAGE | SIF_RANGE;
2737 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2738 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2739 "Vertical scrollbar is visible, should be invisible.\n");
2740 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2741 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2742 si.nPage, si.nMin, si.nMax);
2744 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2745 memset(&si, 0, sizeof(si));
2746 si.cbSize = sizeof(si);
2747 si.fMask = SIF_PAGE | SIF_RANGE;
2748 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2749 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2750 "Vertical scrollbar is invisible, should be visible.\n");
2751 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2752 "reported page/range is %d (%d..%d)\n",
2753 si.nPage, si.nMin, si.nMax);
2755 DestroyWindow(hwndRichEdit);
2758 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2759 hwndRichEdit = new_richedit(NULL);
2761 #define ENABLE_WS_VSCROLL(hwnd) \
2762 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2763 #define DISABLE_WS_VSCROLL(hwnd) \
2764 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2766 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2767 ENABLE_WS_VSCROLL(hwndRichEdit);
2768 memset(&si, 0, sizeof(si));
2769 si.cbSize = sizeof(si);
2770 si.fMask = SIF_PAGE | SIF_RANGE;
2771 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2772 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2773 "Vertical scrollbar is invisible, should be visible.\n");
2774 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2775 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2776 si.nPage, si.nMin, si.nMax);
2778 /* Ditto, see above */
2779 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2780 memset(&si, 0, sizeof(si));
2781 si.cbSize = sizeof(si);
2782 si.fMask = SIF_PAGE | SIF_RANGE;
2783 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2784 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2785 "Vertical scrollbar is invisible, should be visible.\n");
2786 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2787 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2788 si.nPage, si.nMin, si.nMax);
2790 /* Ditto, see above */
2791 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2792 memset(&si, 0, sizeof(si));
2793 si.cbSize = sizeof(si);
2794 si.fMask = SIF_PAGE | SIF_RANGE;
2795 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2796 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2797 "Vertical scrollbar is invisible, should be visible.\n");
2798 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2799 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2800 si.nPage, si.nMin, si.nMax);
2802 /* Ditto, see above */
2803 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2804 memset(&si, 0, sizeof(si));
2805 si.cbSize = sizeof(si);
2806 si.fMask = SIF_PAGE | SIF_RANGE;
2807 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2808 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2809 "Vertical scrollbar is invisible, should be visible.\n");
2810 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2811 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2812 si.nPage, si.nMin, si.nMax);
2814 /* Ditto, see above */
2815 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2816 memset(&si, 0, sizeof(si));
2817 si.cbSize = sizeof(si);
2818 si.fMask = SIF_PAGE | SIF_RANGE;
2819 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2820 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2821 "Vertical scrollbar is invisible, should be visible.\n");
2822 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2823 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2824 si.nPage, si.nMin, si.nMax);
2826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2827 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2828 memset(&si, 0, sizeof(si));
2829 si.cbSize = sizeof(si);
2830 si.fMask = SIF_PAGE | SIF_RANGE;
2831 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2832 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2833 "Vertical scrollbar is visible, should be invisible.\n");
2834 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2835 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2836 si.nPage, si.nMin, si.nMax);
2838 DestroyWindow(hwndRichEdit);
2840 hwndRichEdit = new_richedit(NULL);
2842 DISABLE_WS_VSCROLL(hwndRichEdit);
2843 memset(&si, 0, sizeof(si));
2844 si.cbSize = sizeof(si);
2845 si.fMask = SIF_PAGE | SIF_RANGE;
2846 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2847 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2848 "Vertical scrollbar is visible, should be invisible.\n");
2849 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2850 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2851 si.nPage, si.nMin, si.nMax);
2853 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2854 memset(&si, 0, sizeof(si));
2855 si.cbSize = sizeof(si);
2856 si.fMask = SIF_PAGE | SIF_RANGE;
2857 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2858 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2859 "Vertical scrollbar is visible, should be invisible.\n");
2860 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2861 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2862 si.nPage, si.nMin, si.nMax);
2864 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2865 memset(&si, 0, sizeof(si));
2866 si.cbSize = sizeof(si);
2867 si.fMask = SIF_PAGE | SIF_RANGE;
2868 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2869 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2870 "Vertical scrollbar is visible, should be invisible.\n");
2871 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2872 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2873 si.nPage, si.nMin, si.nMax);
2875 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2876 memset(&si, 0, sizeof(si));
2877 si.cbSize = sizeof(si);
2878 si.fMask = SIF_PAGE | SIF_RANGE;
2879 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2880 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2881 "Vertical scrollbar is visible, should be invisible.\n");
2882 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2883 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2884 si.nPage, si.nMin, si.nMax);
2886 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2887 memset(&si, 0, sizeof(si));
2888 si.cbSize = sizeof(si);
2889 si.fMask = SIF_PAGE | SIF_RANGE;
2890 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2891 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2892 "Vertical scrollbar is invisible, should be visible.\n");
2893 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2894 "reported page/range is %d (%d..%d)\n",
2895 si.nPage, si.nMin, si.nMax);
2897 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2898 DISABLE_WS_VSCROLL(hwndRichEdit);
2899 memset(&si, 0, sizeof(si));
2900 si.cbSize = sizeof(si);
2901 si.fMask = SIF_PAGE | SIF_RANGE;
2902 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2903 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2904 "Vertical scrollbar is visible, should be invisible.\n");
2905 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2906 "reported page/range is %d (%d..%d)\n",
2907 si.nPage, si.nMin, si.nMax);
2909 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2910 memset(&si, 0, sizeof(si));
2911 si.cbSize = sizeof(si);
2912 si.fMask = SIF_PAGE | SIF_RANGE;
2913 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2914 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2915 "Vertical scrollbar is visible, should be invisible.\n");
2916 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2917 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2918 si.nPage, si.nMin, si.nMax);
2920 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2921 memset(&si, 0, sizeof(si));
2922 si.cbSize = sizeof(si);
2923 si.fMask = SIF_PAGE | SIF_RANGE;
2924 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2925 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2926 "Vertical scrollbar is invisible, should be visible.\n");
2927 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2928 "reported page/range is %d (%d..%d)\n",
2929 si.nPage, si.nMin, si.nMax);
2931 DISABLE_WS_VSCROLL(hwndRichEdit);
2932 memset(&si, 0, sizeof(si));
2933 si.cbSize = sizeof(si);
2934 si.fMask = SIF_PAGE | SIF_RANGE;
2935 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2936 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2937 "Vertical scrollbar is visible, should be invisible.\n");
2938 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2939 "reported page/range is %d (%d..%d)\n",
2940 si.nPage, si.nMin, si.nMax);
2942 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2943 EM_SCROLL will make visible any forcefully invisible scrollbar */
2944 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2945 memset(&si, 0, sizeof(si));
2946 si.cbSize = sizeof(si);
2947 si.fMask = SIF_PAGE | SIF_RANGE;
2948 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2949 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2950 "Vertical scrollbar is invisible, should be visible.\n");
2951 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2952 "reported page/range is %d (%d..%d)\n",
2953 si.nPage, si.nMin, si.nMax);
2955 DISABLE_WS_VSCROLL(hwndRichEdit);
2956 memset(&si, 0, sizeof(si));
2957 si.cbSize = sizeof(si);
2958 si.fMask = SIF_PAGE | SIF_RANGE;
2959 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2960 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2961 "Vertical scrollbar is visible, should be invisible.\n");
2962 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2963 "reported page/range is %d (%d..%d)\n",
2964 si.nPage, si.nMin, si.nMax);
2966 /* Again, EM_SCROLL, with SB_LINEUP */
2967 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2968 memset(&si, 0, sizeof(si));
2969 si.cbSize = sizeof(si);
2970 si.fMask = SIF_PAGE | SIF_RANGE;
2971 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2972 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2973 "Vertical scrollbar is invisible, should be visible.\n");
2974 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2975 "reported page/range is %d (%d..%d)\n",
2976 si.nPage, si.nMin, si.nMax);
2978 DestroyWindow(hwndRichEdit);
2980 /* This window proc models what is going on with Corman Lisp 3.0.
2981 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
2982 force the scrollbar into visibility. Recursion should NOT happen
2983 as a result of this action.
2985 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
2986 if (r) {
2987 richeditProc = cls.lpfnWndProc;
2988 cls.lpfnWndProc = RicheditStupidOverrideProcA;
2989 cls.lpszClassName = "RicheditStupidOverride";
2990 if(!RegisterClassA(&cls)) assert(0);
2992 recursionLevel = 0;
2993 WM_SIZE_recursionLevel = 0;
2994 bailedOutOfRecursion = FALSE;
2995 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
2996 ok(!bailedOutOfRecursion,
2997 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2999 recursionLevel = 0;
3000 WM_SIZE_recursionLevel = 0;
3001 bailedOutOfRecursion = FALSE;
3002 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3003 ok(!bailedOutOfRecursion,
3004 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3006 /* Unblock window in order to process WM_DESTROY */
3007 recursionLevel = 0;
3008 bailedOutOfRecursion = FALSE;
3009 WM_SIZE_recursionLevel = 0;
3010 DestroyWindow(hwndRichEdit);
3014 static void test_EM_SETUNDOLIMIT(void)
3016 /* cases we test for:
3017 * default behaviour - limiting at 100 undo's
3018 * undo disabled - setting a limit of 0
3019 * undo limited - undo limit set to some to some number, like 2
3020 * bad input - sending a negative number should default to 100 undo's */
3022 HWND hwndRichEdit = new_richedit(NULL);
3023 CHARRANGE cr;
3024 int i;
3025 int result;
3027 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3028 cr.cpMin = 0;
3029 cr.cpMax = 1;
3030 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3031 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3032 also, multiple pastes don't combine like WM_CHAR would */
3033 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3035 /* first case - check the default */
3036 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3037 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3038 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3039 for (i=0; i<100; i++) /* Undo 100 of them */
3040 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3041 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3042 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3044 /* second case - cannot undo */
3045 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3046 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3047 SendMessage(hwndRichEdit,
3048 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3049 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3050 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3052 /* third case - set it to an arbitrary number */
3053 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3054 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3055 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3056 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3057 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3058 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3059 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3060 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3061 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3062 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3063 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3064 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3065 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3066 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3068 /* fourth case - setting negative numbers should default to 100 undos */
3069 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3070 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3071 ok (result == 100,
3072 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3074 DestroyWindow(hwndRichEdit);
3077 static void test_ES_PASSWORD(void)
3079 /* This isn't hugely testable, so we're just going to run it through its paces */
3081 HWND hwndRichEdit = new_richedit(NULL);
3082 WCHAR result;
3084 /* First, check the default of a regular control */
3085 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3086 ok (result == 0,
3087 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3089 /* Now, set it to something normal */
3090 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3091 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3092 ok (result == 120,
3093 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3095 /* Now, set it to something odd */
3096 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3097 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3098 ok (result == 1234,
3099 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3100 DestroyWindow(hwndRichEdit);
3103 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3104 LPBYTE pbBuff,
3105 LONG cb,
3106 LONG *pcb)
3108 char** str = (char**)dwCookie;
3109 *pcb = cb;
3110 if (*pcb > 0) {
3111 memcpy(*str, pbBuff, *pcb);
3112 *str += *pcb;
3114 return 0;
3117 static void test_WM_SETTEXT()
3119 HWND hwndRichEdit = new_richedit(NULL);
3120 const char * TestItem1 = "TestSomeText";
3121 const char * TestItem2 = "TestSomeText\r";
3122 const char * TestItem2_after = "TestSomeText\r\n";
3123 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3124 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3125 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3126 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3127 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3128 const char * TestItem5_after = "TestSomeText TestSomeText";
3129 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3130 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3131 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3132 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3134 char buf[1024] = {0};
3135 LRESULT result;
3136 EDITSTREAM es;
3137 char * p;
3139 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3140 any solitary \r to be converted to \r\n on return. Properly paired
3141 \r\n are not affected. It also shows that the special sequence \r\r\n
3142 gets converted to a single space.
3145 #define TEST_SETTEXT(a, b) \
3146 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3147 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3148 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3149 ok (result == lstrlen(buf), \
3150 "WM_GETTEXT returned %ld instead of expected %u\n", \
3151 result, lstrlen(buf)); \
3152 result = strcmp(b, buf); \
3153 ok(result == 0, \
3154 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3156 TEST_SETTEXT(TestItem1, TestItem1)
3157 TEST_SETTEXT(TestItem2, TestItem2_after)
3158 TEST_SETTEXT(TestItem3, TestItem3_after)
3159 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3160 TEST_SETTEXT(TestItem4, TestItem4_after)
3161 TEST_SETTEXT(TestItem5, TestItem5_after)
3162 TEST_SETTEXT(TestItem6, TestItem6_after)
3163 TEST_SETTEXT(TestItem7, TestItem7_after)
3165 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3166 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3167 p = buf;
3168 es.dwCookie = (DWORD_PTR)&p;
3169 es.dwError = 0;
3170 es.pfnCallback = test_WM_SETTEXT_esCallback;
3171 memset(buf, 0, sizeof(buf));
3172 SendMessage(hwndRichEdit, EM_STREAMOUT,
3173 (WPARAM)(SF_RTF), (LPARAM)&es);
3174 trace("EM_STREAMOUT produced: \n%s\n", buf);
3175 TEST_SETTEXT(buf, TestItem1)
3177 #undef TEST_SETTEXT
3178 DestroyWindow(hwndRichEdit);
3181 static void test_EM_STREAMOUT(void)
3183 HWND hwndRichEdit = new_richedit(NULL);
3184 int r;
3185 EDITSTREAM es;
3186 char buf[1024] = {0};
3187 char * p;
3189 const char * TestItem1 = "TestSomeText";
3190 const char * TestItem2 = "TestSomeText\r";
3191 const char * TestItem3 = "TestSomeText\r\n";
3193 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3194 p = buf;
3195 es.dwCookie = (DWORD_PTR)&p;
3196 es.dwError = 0;
3197 es.pfnCallback = test_WM_SETTEXT_esCallback;
3198 memset(buf, 0, sizeof(buf));
3199 SendMessage(hwndRichEdit, EM_STREAMOUT,
3200 (WPARAM)(SF_TEXT), (LPARAM)&es);
3201 r = strlen(buf);
3202 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3203 ok(strcmp(buf, TestItem1) == 0,
3204 "streamed text different, got %s\n", buf);
3206 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3207 p = buf;
3208 es.dwCookie = (DWORD_PTR)&p;
3209 es.dwError = 0;
3210 es.pfnCallback = test_WM_SETTEXT_esCallback;
3211 memset(buf, 0, sizeof(buf));
3212 SendMessage(hwndRichEdit, EM_STREAMOUT,
3213 (WPARAM)(SF_TEXT), (LPARAM)&es);
3214 r = strlen(buf);
3215 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3216 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3217 ok(strcmp(buf, TestItem3) == 0,
3218 "streamed text different from, got %s\n", buf);
3219 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3220 p = buf;
3221 es.dwCookie = (DWORD_PTR)&p;
3222 es.dwError = 0;
3223 es.pfnCallback = test_WM_SETTEXT_esCallback;
3224 memset(buf, 0, sizeof(buf));
3225 SendMessage(hwndRichEdit, EM_STREAMOUT,
3226 (WPARAM)(SF_TEXT), (LPARAM)&es);
3227 r = strlen(buf);
3228 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3229 ok(strcmp(buf, TestItem3) == 0,
3230 "streamed text different, got %s\n", buf);
3232 DestroyWindow(hwndRichEdit);
3235 static void test_EM_SETTEXTEX(void)
3237 HWND hwndRichEdit = new_richedit(NULL);
3238 SETTEXTEX setText;
3239 GETTEXTEX getText;
3240 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3241 'S', 'o', 'm', 'e',
3242 'T', 'e', 'x', 't', 0};
3243 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3244 't', 'S', 'o', 'm',
3245 'e', 'T', 'e', 'x',
3246 't', 't', 'S', 'o',
3247 'm', 'e', 'T', 'e',
3248 'x', 't', 0};
3249 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3250 '\r','t','S','o','m','e','T','e','x','t',0};
3251 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3252 'S', 'o', 'm', 'e',
3253 'T', 'e', 'x', 't',
3254 '\r', 0};
3255 const char * TestItem2_after = "TestSomeText\r\n";
3256 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3257 'S', 'o', 'm', 'e',
3258 'T', 'e', 'x', 't',
3259 '\r','\n','\r','\n', 0};
3260 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3261 'S', 'o', 'm', 'e',
3262 'T', 'e', 'x', 't',
3263 '\n','\n', 0};
3264 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3265 'S', 'o', 'm', 'e',
3266 'T', 'e', 'x', 't',
3267 '\r','\r', 0};
3268 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3269 'S', 'o', 'm', 'e',
3270 'T', 'e', 'x', 't',
3271 '\r','\r','\n','\r',
3272 '\n', 0};
3273 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3274 'S', 'o', 'm', 'e',
3275 'T', 'e', 'x', 't',
3276 ' ','\r', 0};
3277 #define MAX_BUF_LEN 1024
3278 WCHAR buf[MAX_BUF_LEN];
3279 char bufACP[MAX_BUF_LEN];
3280 char * p;
3281 int result;
3282 CHARRANGE cr;
3283 EDITSTREAM es;
3285 setText.codepage = 1200; /* no constant for unicode */
3286 getText.codepage = 1200; /* no constant for unicode */
3287 getText.cb = MAX_BUF_LEN;
3288 getText.flags = GT_DEFAULT;
3289 getText.lpDefaultChar = NULL;
3290 getText.lpUsedDefChar = NULL;
3292 setText.flags = 0;
3293 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3294 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3295 ok(lstrcmpW(buf, TestItem1) == 0,
3296 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3298 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3299 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3301 setText.codepage = 1200; /* no constant for unicode */
3302 getText.codepage = 1200; /* no constant for unicode */
3303 getText.cb = MAX_BUF_LEN;
3304 getText.flags = GT_DEFAULT;
3305 getText.lpDefaultChar = NULL;
3306 getText.lpUsedDefChar = NULL;
3307 setText.flags = 0;
3308 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3309 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3310 ok(lstrcmpW(buf, TestItem2) == 0,
3311 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3313 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3314 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3315 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3316 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3318 /* Baseline test for just-enough buffer space for string */
3319 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3320 getText.codepage = 1200; /* no constant for unicode */
3321 getText.flags = GT_DEFAULT;
3322 getText.lpDefaultChar = NULL;
3323 getText.lpUsedDefChar = NULL;
3324 memset(buf, 0, MAX_BUF_LEN);
3325 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3326 ok(lstrcmpW(buf, TestItem2) == 0,
3327 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3329 /* When there is enough space for one character, but not both, of the CRLF
3330 pair at the end of the string, the CR is not copied at all. That is,
3331 the caller must not see CRLF pairs truncated to CR at the end of the
3332 string.
3334 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3335 getText.codepage = 1200; /* no constant for unicode */
3336 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3337 getText.lpDefaultChar = NULL;
3338 getText.lpUsedDefChar = NULL;
3339 memset(buf, 0, MAX_BUF_LEN);
3340 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3341 ok(lstrcmpW(buf, TestItem1) == 0,
3342 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3345 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3346 setText.codepage = 1200; /* no constant for unicode */
3347 getText.codepage = 1200; /* no constant for unicode */
3348 getText.cb = MAX_BUF_LEN;
3349 getText.flags = GT_DEFAULT;
3350 getText.lpDefaultChar = NULL;
3351 getText.lpUsedDefChar = NULL;
3352 setText.flags = 0;
3353 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3354 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3355 ok(lstrcmpW(buf, TestItem3_after) == 0,
3356 "EM_SETTEXTEX did not convert properly\n");
3358 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3359 setText.codepage = 1200; /* no constant for unicode */
3360 getText.codepage = 1200; /* no constant for unicode */
3361 getText.cb = MAX_BUF_LEN;
3362 getText.flags = GT_DEFAULT;
3363 getText.lpDefaultChar = NULL;
3364 getText.lpUsedDefChar = NULL;
3365 setText.flags = 0;
3366 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3367 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3368 ok(lstrcmpW(buf, TestItem3_after) == 0,
3369 "EM_SETTEXTEX did not convert properly\n");
3371 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3372 setText.codepage = 1200; /* no constant for unicode */
3373 getText.codepage = 1200; /* no constant for unicode */
3374 getText.cb = MAX_BUF_LEN;
3375 getText.flags = GT_DEFAULT;
3376 getText.lpDefaultChar = NULL;
3377 getText.lpUsedDefChar = NULL;
3378 setText.flags = 0;
3379 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3380 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3381 ok(lstrcmpW(buf, TestItem4_after) == 0,
3382 "EM_SETTEXTEX did not convert properly\n");
3384 /* !ST_SELECTION && Unicode && !\rtf */
3385 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3386 (WPARAM)&setText, (LPARAM) NULL);
3387 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3389 ok (result == 1,
3390 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3391 ok(lstrlenW(buf) == 0,
3392 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3394 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3395 setText.flags = 0;
3396 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3397 /* select some text */
3398 cr.cpMax = 1;
3399 cr.cpMin = 3;
3400 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3401 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3402 setText.flags = ST_SELECTION;
3403 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3404 (WPARAM)&setText, (LPARAM) NULL);
3405 ok(result == 0,
3406 "EM_SETTEXTEX with NULL lParam to replace selection"
3407 " with no text should return 0. Got %i\n",
3408 result);
3410 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3411 setText.flags = 0;
3412 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3413 /* select some text */
3414 cr.cpMax = 1;
3415 cr.cpMin = 3;
3416 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3417 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3418 setText.flags = ST_SELECTION;
3419 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3420 (WPARAM)&setText, (LPARAM) TestItem1);
3421 /* get text */
3422 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3423 ok(result == lstrlenW(TestItem1),
3424 "EM_SETTEXTEX with NULL lParam to replace selection"
3425 " with no text should return 0. Got %i\n",
3426 result);
3427 ok(lstrlenW(buf) == 22,
3428 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3429 lstrlenW(buf) );
3431 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3432 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3433 p = (char *)buf;
3434 es.dwCookie = (DWORD_PTR)&p;
3435 es.dwError = 0;
3436 es.pfnCallback = test_WM_SETTEXT_esCallback;
3437 memset(buf, 0, sizeof(buf));
3438 SendMessage(hwndRichEdit, EM_STREAMOUT,
3439 (WPARAM)(SF_RTF), (LPARAM)&es);
3440 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
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 = 0;
3451 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3452 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3453 ok(lstrcmpW(buf, TestItem1) == 0,
3454 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3456 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3457 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3458 p = (char *)buf;
3459 es.dwCookie = (DWORD_PTR)&p;
3460 es.dwError = 0;
3461 es.pfnCallback = test_WM_SETTEXT_esCallback;
3462 memset(buf, 0, sizeof(buf));
3463 SendMessage(hwndRichEdit, EM_STREAMOUT,
3464 (WPARAM)(SF_RTF), (LPARAM)&es);
3465 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3467 /* select some text */
3468 cr.cpMax = 1;
3469 cr.cpMin = 3;
3470 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3472 /* ST_SELECTION && !Unicode && \rtf */
3473 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3474 getText.codepage = 1200; /* no constant for unicode */
3475 getText.cb = MAX_BUF_LEN;
3476 getText.flags = GT_DEFAULT;
3477 getText.lpDefaultChar = NULL;
3478 getText.lpUsedDefChar = NULL;
3480 setText.flags = ST_SELECTION;
3481 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3482 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3483 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3485 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3486 setText.codepage = 1200; /* no constant for unicode */
3487 getText.codepage = CP_ACP;
3488 getText.cb = MAX_BUF_LEN;
3490 setText.flags = 0;
3491 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3492 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3494 /* select some text */
3495 cr.cpMax = 1;
3496 cr.cpMin = 3;
3497 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3499 /* ST_SELECTION && !Unicode && !\rtf */
3500 setText.codepage = CP_ACP;
3501 getText.codepage = 1200; /* no constant for unicode */
3502 getText.cb = MAX_BUF_LEN;
3503 getText.flags = GT_DEFAULT;
3504 getText.lpDefaultChar = NULL;
3505 getText.lpUsedDefChar = NULL;
3507 setText.flags = ST_SELECTION;
3508 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3509 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3510 ok(lstrcmpW(buf, TestItem1alt) == 0,
3511 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3512 " using ST_SELECTION and non-Unicode\n");
3515 DestroyWindow(hwndRichEdit);
3518 static void test_EM_LIMITTEXT(void)
3520 int ret;
3522 HWND hwndRichEdit = new_richedit(NULL);
3524 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3525 * about setting the length to -1 for multiline edit controls doesn't happen.
3528 /* Don't check default gettextlimit case. That's done in other tests */
3530 /* Set textlimit to 100 */
3531 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3532 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3533 ok (ret == 100,
3534 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3536 /* Set textlimit to 0 */
3537 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3538 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3539 ok (ret == 65536,
3540 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3542 /* Set textlimit to -1 */
3543 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3544 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3545 ok (ret == -1,
3546 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3548 /* Set textlimit to -2 */
3549 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3550 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3551 ok (ret == -2,
3552 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3554 DestroyWindow (hwndRichEdit);
3558 static void test_EM_EXLIMITTEXT(void)
3560 int i, selBegin, selEnd, len1, len2;
3561 int result;
3562 char text[1024 + 1];
3563 char buffer[1024 + 1];
3564 int textlimit = 0; /* multiple of 100 */
3565 HWND hwndRichEdit = new_richedit(NULL);
3567 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3568 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3570 textlimit = 256000;
3571 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3572 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3573 /* set higher */
3574 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3576 textlimit = 1000;
3577 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3578 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3579 /* set lower */
3580 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3582 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3583 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3584 /* default for WParam = 0 */
3585 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3587 textlimit = sizeof(text)-1;
3588 memset(text, 'W', textlimit);
3589 text[sizeof(text)-1] = 0;
3590 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3591 /* maxed out text */
3592 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3594 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3595 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3596 len1 = selEnd - selBegin;
3598 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3599 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3600 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3601 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3602 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3603 len2 = selEnd - selBegin;
3605 ok(len1 != len2,
3606 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3607 len1,len2,i);
3609 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3610 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3611 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3612 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3613 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3614 len1 = selEnd - selBegin;
3616 ok(len1 != len2,
3617 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3618 len1,len2,i);
3620 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3621 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3622 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3623 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3624 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3625 len2 = selEnd - selBegin;
3627 ok(len1 == len2,
3628 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3629 len1,len2,i);
3631 /* set text up to the limit, select all the text, then add a char */
3632 textlimit = 5;
3633 memset(text, 'W', textlimit);
3634 text[textlimit] = 0;
3635 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3636 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3637 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3638 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3639 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3640 result = strcmp(buffer, "A");
3641 ok(0 == result, "got string = \"%s\"\n", buffer);
3643 /* WM_SETTEXT not limited */
3644 textlimit = 10;
3645 memset(text, 'W', textlimit);
3646 text[textlimit] = 0;
3647 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3648 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3649 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3650 i = strlen(buffer);
3651 ok(10 == i, "expected 10 chars\n");
3652 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3653 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3655 /* try inserting more text at end */
3656 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3657 ok(0 == i, "WM_CHAR wasn't processed\n");
3658 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3659 i = strlen(buffer);
3660 ok(10 == i, "expected 10 chars, got %i\n", i);
3661 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3662 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3664 /* try inserting text at beginning */
3665 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3666 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3667 ok(0 == i, "WM_CHAR wasn't processed\n");
3668 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3669 i = strlen(buffer);
3670 ok(10 == i, "expected 10 chars, got %i\n", i);
3671 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3672 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3674 /* WM_CHAR is limited */
3675 textlimit = 1;
3676 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3677 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3678 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3679 ok(0 == i, "WM_CHAR wasn't processed\n");
3680 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3681 ok(0 == i, "WM_CHAR wasn't processed\n");
3682 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3683 i = strlen(buffer);
3684 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3686 DestroyWindow(hwndRichEdit);
3689 static void test_EM_GETLIMITTEXT(void)
3691 int i;
3692 HWND hwndRichEdit = new_richedit(NULL);
3694 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3695 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3697 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3698 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3699 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3701 DestroyWindow(hwndRichEdit);
3704 static void test_WM_SETFONT(void)
3706 /* There is no invalid input or error conditions for this function.
3707 * NULL wParam and lParam just fall back to their default values
3708 * It should be noted that even if you use a gibberish name for your fonts
3709 * here, it will still work because the name is stored. They will display as
3710 * System, but will report their name to be whatever they were created as */
3712 HWND hwndRichEdit = new_richedit(NULL);
3713 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3714 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3715 FF_DONTCARE, "Marlett");
3716 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3717 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3718 FF_DONTCARE, "MS Sans Serif");
3719 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3720 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3721 FF_DONTCARE, "Courier");
3722 LOGFONTA sentLogFont;
3723 CHARFORMAT2A returnedCF2A;
3725 returnedCF2A.cbSize = sizeof(returnedCF2A);
3727 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3728 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
3729 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3731 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3732 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3733 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3734 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3736 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
3737 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3738 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3739 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3740 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3741 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3743 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
3744 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3745 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3746 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3747 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3748 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3750 /* This last test is special since we send in NULL. We clear the variables
3751 * and just compare to "System" instead of the sent in font name. */
3752 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3753 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3754 returnedCF2A.cbSize = sizeof(returnedCF2A);
3756 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
3757 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3758 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3759 ok (!strcmp("System",returnedCF2A.szFaceName),
3760 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3762 DestroyWindow(hwndRichEdit);
3766 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3767 LPBYTE pbBuff,
3768 LONG cb,
3769 LONG *pcb)
3771 const char** str = (const char**)dwCookie;
3772 int size = strlen(*str);
3773 if(size > 3) /* let's make it piecemeal for fun */
3774 size = 3;
3775 *pcb = cb;
3776 if (*pcb > size) {
3777 *pcb = size;
3779 if (*pcb > 0) {
3780 memcpy(pbBuff, *str, *pcb);
3781 *str += *pcb;
3783 return 0;
3786 static void test_EM_GETMODIFY(void)
3788 HWND hwndRichEdit = new_richedit(NULL);
3789 LRESULT result;
3790 SETTEXTEX setText;
3791 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3792 'S', 'o', 'm', 'e',
3793 'T', 'e', 'x', 't', 0};
3794 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3795 'S', 'o', 'm', 'e',
3796 'O', 't', 'h', 'e', 'r',
3797 'T', 'e', 'x', 't', 0};
3798 const char* streamText = "hello world";
3799 CHARFORMAT2 cf2;
3800 PARAFORMAT2 pf2;
3801 EDITSTREAM es;
3803 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3804 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3805 FF_DONTCARE, "Courier");
3807 setText.codepage = 1200; /* no constant for unicode */
3808 setText.flags = ST_KEEPUNDO;
3811 /* modify flag shouldn't be set when richedit is first created */
3812 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3813 ok (result == 0,
3814 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
3816 /* setting modify flag should actually set it */
3817 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
3818 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3819 ok (result != 0,
3820 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
3822 /* clearing modify flag should actually clear it */
3823 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3824 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3825 ok (result == 0,
3826 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
3828 /* setting font doesn't change modify flag */
3829 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3830 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
3831 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3832 ok (result == 0,
3833 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
3835 /* setting text should set modify flag */
3836 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3837 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3838 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3839 ok (result != 0,
3840 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
3842 /* undo previous text doesn't reset modify flag */
3843 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3844 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3845 ok (result != 0,
3846 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
3848 /* set text with no flag to keep undo stack should not set modify flag */
3849 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3850 setText.flags = 0;
3851 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3852 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3853 ok (result == 0,
3854 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
3856 /* WM_SETTEXT doesn't modify */
3857 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3858 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3859 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3860 ok (result == 0,
3861 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3863 /* clear the text */
3864 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3865 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3866 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3867 ok (result == 0,
3868 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3870 /* replace text */
3871 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3872 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3873 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3874 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3875 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3876 ok (result != 0,
3877 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3879 /* copy/paste text 1 */
3880 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3881 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3882 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3883 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3884 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3885 ok (result != 0,
3886 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3888 /* copy/paste text 2 */
3889 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3890 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3891 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3892 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3893 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3894 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3895 ok (result != 0,
3896 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3898 /* press char */
3899 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3900 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3901 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3902 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3903 ok (result != 0,
3904 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3906 /* press del */
3907 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3908 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3909 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
3910 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3911 ok (result != 0,
3912 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3914 /* set char format */
3915 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3916 cf2.cbSize = sizeof(CHARFORMAT2);
3917 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3918 (LPARAM) &cf2);
3919 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3920 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3921 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3922 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3923 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3924 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3925 ok (result != 0,
3926 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3928 /* set para format */
3929 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3930 pf2.cbSize = sizeof(PARAFORMAT2);
3931 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3932 (LPARAM) &pf2);
3933 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3934 pf2.wAlignment = PFA_RIGHT;
3935 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3936 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3937 ok (result == 0,
3938 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3940 /* EM_STREAM */
3941 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3942 es.dwCookie = (DWORD_PTR)&streamText;
3943 es.dwError = 0;
3944 es.pfnCallback = test_EM_GETMODIFY_esCallback;
3945 SendMessage(hwndRichEdit, EM_STREAMIN,
3946 (WPARAM)(SF_TEXT), (LPARAM)&es);
3947 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3948 ok (result != 0,
3949 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
3951 DestroyWindow(hwndRichEdit);
3954 struct exsetsel_s {
3955 long min;
3956 long max;
3957 long expected_retval;
3958 int expected_getsel_start;
3959 int expected_getsel_end;
3960 int _exsetsel_todo_wine;
3961 int _getsel_todo_wine;
3964 const struct exsetsel_s exsetsel_tests[] = {
3965 /* sanity tests */
3966 {5, 10, 10, 5, 10, 0, 0},
3967 {15, 17, 17, 15, 17, 0, 0},
3968 /* test cpMax > strlen() */
3969 {0, 100, 18, 0, 18, 0, 1},
3970 /* test cpMin == cpMax */
3971 {5, 5, 5, 5, 5, 0, 0},
3972 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
3973 {-1, 0, 5, 5, 5, 0, 0},
3974 {-1, 17, 5, 5, 5, 0, 0},
3975 {-1, 18, 5, 5, 5, 0, 0},
3976 /* test cpMin < 0 && cpMax < 0 */
3977 {-1, -1, 17, 17, 17, 0, 0},
3978 {-4, -5, 17, 17, 17, 0, 0},
3979 /* test cMin >=0 && cpMax < 0 (bug 6814) */
3980 {0, -1, 18, 0, 18, 0, 1},
3981 {17, -5, 18, 17, 18, 0, 1},
3982 {18, -3, 17, 17, 17, 0, 0},
3983 /* test if cpMin > cpMax */
3984 {15, 19, 18, 15, 18, 0, 1},
3985 {19, 15, 18, 15, 18, 0, 1}
3988 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
3989 CHARRANGE cr;
3990 long result;
3991 int start, end;
3993 cr.cpMin = setsel->min;
3994 cr.cpMax = setsel->max;
3995 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
3997 if (setsel->_exsetsel_todo_wine) {
3998 todo_wine {
3999 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4001 } else {
4002 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4005 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4007 if (setsel->_getsel_todo_wine) {
4008 todo_wine {
4009 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);
4011 } else {
4012 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);
4016 static void test_EM_EXSETSEL(void)
4018 HWND hwndRichEdit = new_richedit(NULL);
4019 int i;
4020 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4022 /* sending some text to the window */
4023 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4024 /* 01234567890123456*/
4025 /* 10 */
4027 for (i = 0; i < num_tests; i++) {
4028 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4031 DestroyWindow(hwndRichEdit);
4034 static void test_EM_REPLACESEL(int redraw)
4036 HWND hwndRichEdit = new_richedit(NULL);
4037 char buffer[1024] = {0};
4038 int r;
4039 GETTEXTEX getText;
4040 CHARRANGE cr;
4042 /* sending some text to the window */
4043 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4044 /* 01234567890123456*/
4045 /* 10 */
4047 /* FIXME add more tests */
4048 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4049 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
4050 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4051 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4052 r = strcmp(buffer, "testing");
4053 ok(0 == r, "expected %d, got %d\n", 0, r);
4055 DestroyWindow(hwndRichEdit);
4057 hwndRichEdit = new_richedit(NULL);
4059 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4060 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4062 /* Test behavior with carriage returns and newlines */
4063 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4064 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4065 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4066 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4067 r = strcmp(buffer, "RichEdit1");
4068 ok(0 == r, "expected %d, got %d\n", 0, r);
4069 getText.cb = 1024;
4070 getText.codepage = CP_ACP;
4071 getText.flags = GT_DEFAULT;
4072 getText.lpDefaultChar = NULL;
4073 getText.lpUsedDefChar = NULL;
4074 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4075 ok(strcmp(buffer, "RichEdit1") == 0,
4076 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4078 /* Test number of lines reported after EM_REPLACESEL */
4079 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4080 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4082 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4083 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4084 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4085 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4086 r = strcmp(buffer, "RichEdit1\r\n");
4087 ok(0 == r, "expected %d, got %d\n", 0, r);
4088 getText.cb = 1024;
4089 getText.codepage = CP_ACP;
4090 getText.flags = GT_DEFAULT;
4091 getText.lpDefaultChar = NULL;
4092 getText.lpUsedDefChar = NULL;
4093 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4094 ok(strcmp(buffer, "RichEdit1\r") == 0,
4095 "EM_GETTEXTEX returned incorrect string\n");
4097 /* Test number of lines reported after EM_REPLACESEL */
4098 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4099 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4101 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4102 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4103 returns the number of characters *inserted* into the control (after
4104 required conversions), but WinXP's riched20 returns the number of
4105 characters interpreted from the original lParam. Wine's builtin riched20
4106 implements the WinXP behavior.
4108 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4109 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4110 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4111 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4113 /* Test number of lines reported after EM_REPLACESEL */
4114 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4115 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4117 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4118 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4119 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4120 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4122 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4123 r = strcmp(buffer, "RichEdit1\r\n");
4124 ok(0 == r, "expected %d, got %d\n", 0, r);
4125 getText.cb = 1024;
4126 getText.codepage = CP_ACP;
4127 getText.flags = GT_DEFAULT;
4128 getText.lpDefaultChar = NULL;
4129 getText.lpUsedDefChar = NULL;
4130 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4131 ok(strcmp(buffer, "RichEdit1\r") == 0,
4132 "EM_GETTEXTEX returned incorrect string\n");
4134 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4135 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4136 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4137 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4139 /* The following tests show that richedit should handle the special \r\r\n
4140 sequence by turning it into a single space on insertion. However,
4141 EM_REPLACESEL on WinXP returns the number of characters in the original
4142 string.
4145 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4146 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4147 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4148 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4149 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4150 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4151 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4153 /* Test the actual string */
4154 getText.cb = 1024;
4155 getText.codepage = CP_ACP;
4156 getText.flags = GT_DEFAULT;
4157 getText.lpDefaultChar = NULL;
4158 getText.lpUsedDefChar = NULL;
4159 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4160 ok(strcmp(buffer, "\r\r") == 0,
4161 "EM_GETTEXTEX returned incorrect string\n");
4163 /* Test number of lines reported after EM_REPLACESEL */
4164 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4165 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4167 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4168 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4169 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4170 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4171 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4172 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4173 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4174 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4176 /* Test the actual string */
4177 getText.cb = 1024;
4178 getText.codepage = CP_ACP;
4179 getText.flags = GT_DEFAULT;
4180 getText.lpDefaultChar = NULL;
4181 getText.lpUsedDefChar = NULL;
4182 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4183 ok(strcmp(buffer, " ") == 0,
4184 "EM_GETTEXTEX returned incorrect string\n");
4186 /* Test number of lines reported after EM_REPLACESEL */
4187 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4188 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4190 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4191 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4192 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4193 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4194 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4195 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4196 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4197 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4199 /* Test the actual string */
4200 getText.cb = 1024;
4201 getText.codepage = CP_ACP;
4202 getText.flags = GT_DEFAULT;
4203 getText.lpDefaultChar = NULL;
4204 getText.lpUsedDefChar = NULL;
4205 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4206 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4207 "EM_GETTEXTEX returned incorrect string\n");
4209 /* Test number of lines reported after EM_REPLACESEL */
4210 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4211 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4213 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4214 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4215 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4216 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4217 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4218 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4219 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4220 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4222 /* Test the actual string */
4223 getText.cb = 1024;
4224 getText.codepage = CP_ACP;
4225 getText.flags = GT_DEFAULT;
4226 getText.lpDefaultChar = NULL;
4227 getText.lpUsedDefChar = NULL;
4228 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4229 ok(strcmp(buffer, " \r") == 0,
4230 "EM_GETTEXTEX returned incorrect string\n");
4232 /* Test number of lines reported after EM_REPLACESEL */
4233 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4234 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4236 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4237 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4238 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4239 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4240 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4241 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4242 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4243 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4245 /* Test the actual string */
4246 getText.cb = 1024;
4247 getText.codepage = CP_ACP;
4248 getText.flags = GT_DEFAULT;
4249 getText.lpDefaultChar = NULL;
4250 getText.lpUsedDefChar = NULL;
4251 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4252 ok(strcmp(buffer, " \r\r") == 0,
4253 "EM_GETTEXTEX returned incorrect string\n");
4255 /* Test number of lines reported after EM_REPLACESEL */
4256 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4257 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4259 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4260 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4261 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4262 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4263 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4264 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4265 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4266 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4268 /* Test the actual string */
4269 getText.cb = 1024;
4270 getText.codepage = CP_ACP;
4271 getText.flags = GT_DEFAULT;
4272 getText.lpDefaultChar = NULL;
4273 getText.lpUsedDefChar = NULL;
4274 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4275 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4276 "EM_GETTEXTEX returned incorrect string\n");
4278 /* Test number of lines reported after EM_REPLACESEL */
4279 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4280 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4282 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4283 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4284 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4285 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4286 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4287 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4288 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4290 /* Test the actual string */
4291 getText.cb = 1024;
4292 getText.codepage = CP_ACP;
4293 getText.flags = GT_DEFAULT;
4294 getText.lpDefaultChar = NULL;
4295 getText.lpUsedDefChar = NULL;
4296 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4297 ok(strcmp(buffer, "\r\r") == 0,
4298 "EM_GETTEXTEX returned incorrect string\n");
4300 /* Test number of lines reported after EM_REPLACESEL */
4301 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4302 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4304 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4305 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4306 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4307 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4308 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4309 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4310 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4311 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4313 /* Test the actual string */
4314 getText.cb = 1024;
4315 getText.codepage = CP_ACP;
4316 getText.flags = GT_DEFAULT;
4317 getText.lpDefaultChar = NULL;
4318 getText.lpUsedDefChar = NULL;
4319 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4320 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4321 "EM_GETTEXTEX returned incorrect string\n");
4323 /* Test number of lines reported after EM_REPLACESEL */
4324 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4325 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4327 if (!redraw)
4328 /* This is needed to avoid interferring with keybd_event calls
4329 * on other tests that simulate keyboard events. */
4330 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4332 DestroyWindow(hwndRichEdit);
4335 static void test_WM_PASTE(void)
4337 int result;
4338 char buffer[1024] = {0};
4339 const char* text1 = "testing paste\r";
4340 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4341 const char* text1_after = "testing paste\r\n";
4342 const char* text2 = "testing paste\r\rtesting paste";
4343 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4344 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4345 HWND hwndRichEdit = new_richedit(NULL);
4347 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
4348 messages, probably because it inspects the keyboard state itself.
4349 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
4352 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4353 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4354 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4355 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4356 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4358 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4359 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4361 SEND_CTRL_C(hwndRichEdit); /* Copy */
4362 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4363 SEND_CTRL_V(hwndRichEdit); /* Paste */
4364 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4365 /* Pasted text should be visible at this step */
4366 result = strcmp(text1_step1, buffer);
4367 ok(result == 0,
4368 "test paste: strcmp = %i\n", result);
4369 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4370 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4371 /* Text should be the same as before (except for \r -> \r\n conversion) */
4372 result = strcmp(text1_after, buffer);
4373 ok(result == 0,
4374 "test paste: strcmp = %i\n", result);
4376 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4377 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4378 SEND_CTRL_C(hwndRichEdit); /* Copy */
4379 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4380 SEND_CTRL_V(hwndRichEdit); /* Paste */
4381 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4382 /* Pasted text should be visible at this step */
4383 result = strcmp(text3, buffer);
4384 ok(result == 0,
4385 "test paste: strcmp = %i\n", result);
4386 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4387 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4388 /* Text should be the same as before (except for \r -> \r\n conversion) */
4389 result = strcmp(text2_after, buffer);
4390 ok(result == 0,
4391 "test paste: strcmp = %i\n", result);
4392 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4393 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4394 /* Text should revert to post-paste state */
4395 result = strcmp(buffer,text3);
4396 ok(result == 0,
4397 "test paste: strcmp = %i\n", result);
4399 DestroyWindow(hwndRichEdit);
4402 static void test_EM_FORMATRANGE(void)
4404 int r;
4405 FORMATRANGE fr;
4406 HDC hdc;
4407 HWND hwndRichEdit = new_richedit(NULL);
4409 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4411 hdc = GetDC(hwndRichEdit);
4412 ok(hdc != NULL, "Could not get HDC\n");
4414 fr.hdc = fr.hdcTarget = hdc;
4415 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4416 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4417 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4418 fr.chrg.cpMin = 0;
4419 fr.chrg.cpMax = 20;
4421 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4422 todo_wine {
4423 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4426 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4427 todo_wine {
4428 ok(r == 20 || r == 9, "EM_FORMATRANGE expect 20 or 9, got %d\n", r);
4431 fr.chrg.cpMin = 0;
4432 fr.chrg.cpMax = 10;
4434 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4435 todo_wine {
4436 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4439 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4440 todo_wine {
4441 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4444 DestroyWindow(hwndRichEdit);
4447 static int nCallbackCount = 0;
4449 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4450 LONG cb, LONG* pcb)
4452 const char text[] = {'t','e','s','t'};
4454 if (sizeof(text) <= cb)
4456 if ((int)dwCookie != nCallbackCount)
4458 *pcb = 0;
4459 return 0;
4462 memcpy (pbBuff, text, sizeof(text));
4463 *pcb = sizeof(text);
4465 nCallbackCount++;
4467 return 0;
4469 else
4470 return 1; /* indicates callback failed */
4473 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4474 LPBYTE pbBuff,
4475 LONG cb,
4476 LONG *pcb)
4478 const char** str = (const char**)dwCookie;
4479 int size = strlen(*str);
4480 *pcb = cb;
4481 if (*pcb > size) {
4482 *pcb = size;
4484 if (*pcb > 0) {
4485 memcpy(pbBuff, *str, *pcb);
4486 *str += *pcb;
4488 return 0;
4491 struct StringWithLength {
4492 int length;
4493 char *buffer;
4496 /* This callback is used to handled the null characters in a string. */
4497 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4498 LPBYTE pbBuff,
4499 LONG cb,
4500 LONG *pcb)
4502 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4503 int size = str->length;
4504 *pcb = cb;
4505 if (*pcb > size) {
4506 *pcb = size;
4508 if (*pcb > 0) {
4509 memcpy(pbBuff, str->buffer, *pcb);
4510 str->buffer += *pcb;
4511 str->length -= *pcb;
4513 return 0;
4516 static void test_EM_STREAMIN(void)
4518 HWND hwndRichEdit = new_richedit(NULL);
4519 LRESULT result;
4520 EDITSTREAM es;
4521 char buffer[1024] = {0};
4523 const char * streamText0 = "{\\rtf1 TestSomeText}";
4524 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4525 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4527 const char * streamText1 =
4528 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
4529 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
4530 "}\r\n";
4532 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4533 const char * streamText2 =
4534 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
4535 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
4536 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
4537 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
4538 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
4539 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
4540 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4542 const char * streamText3 = "RichEdit1";
4544 struct StringWithLength cookieForStream4;
4545 const char * streamText4 =
4546 "This text just needs to be long enough to cause run to be split onto "\
4547 "two separate lines and make sure the null terminating character is "\
4548 "handled properly.\0";
4549 int length4 = strlen(streamText4) + 1;
4550 cookieForStream4.buffer = (char *)streamText4;
4551 cookieForStream4.length = length4;
4553 /* Minimal test without \par at the end */
4554 es.dwCookie = (DWORD_PTR)&streamText0;
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 == 12,
4562 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4563 result = strcmp (buffer,"TestSomeText");
4564 ok (result == 0,
4565 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4566 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4568 /* Native richedit 2.0 ignores last \par */
4569 es.dwCookie = (DWORD_PTR)&streamText0a;
4570 es.dwError = 0;
4571 es.pfnCallback = test_EM_STREAMIN_esCallback;
4572 SendMessage(hwndRichEdit, EM_STREAMIN,
4573 (WPARAM)(SF_RTF), (LPARAM)&es);
4575 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4576 ok (result == 12,
4577 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4578 result = strcmp (buffer,"TestSomeText");
4579 ok (result == 0,
4580 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4581 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4583 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4584 es.dwCookie = (DWORD_PTR)&streamText0b;
4585 es.dwError = 0;
4586 es.pfnCallback = test_EM_STREAMIN_esCallback;
4587 SendMessage(hwndRichEdit, EM_STREAMIN,
4588 (WPARAM)(SF_RTF), (LPARAM)&es);
4590 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4591 ok (result == 14,
4592 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4593 result = strcmp (buffer,"TestSomeText\r\n");
4594 ok (result == 0,
4595 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4596 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4598 es.dwCookie = (DWORD_PTR)&streamText1;
4599 es.dwError = 0;
4600 es.pfnCallback = test_EM_STREAMIN_esCallback;
4601 SendMessage(hwndRichEdit, EM_STREAMIN,
4602 (WPARAM)(SF_RTF), (LPARAM)&es);
4604 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4605 ok (result == 12,
4606 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4607 result = strcmp (buffer,"TestSomeText");
4608 ok (result == 0,
4609 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4610 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4612 es.dwCookie = (DWORD_PTR)&streamText2;
4613 es.dwError = 0;
4614 SendMessage(hwndRichEdit, EM_STREAMIN,
4615 (WPARAM)(SF_RTF), (LPARAM)&es);
4617 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4618 ok (result == 0,
4619 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4620 ok (strlen(buffer) == 0,
4621 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4622 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4624 es.dwCookie = (DWORD_PTR)&streamText3;
4625 es.dwError = 0;
4626 SendMessage(hwndRichEdit, EM_STREAMIN,
4627 (WPARAM)(SF_RTF), (LPARAM)&es);
4629 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4630 ok (result == 0,
4631 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4632 ok (strlen(buffer) == 0,
4633 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4634 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4636 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4637 es.dwError = 0;
4638 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4639 SendMessage(hwndRichEdit, EM_STREAMIN,
4640 (WPARAM)(SF_TEXT), (LPARAM)&es);
4642 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4643 ok (result == length4,
4644 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4645 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4647 DestroyWindow(hwndRichEdit);
4650 static void test_EM_StreamIn_Undo(void)
4652 /* The purpose of this test is to determine when a EM_StreamIn should be
4653 * undoable. This is important because WM_PASTE currently uses StreamIn and
4654 * pasting should always be undoable but streaming isn't always.
4656 * cases to test:
4657 * StreamIn plain text without SFF_SELECTION.
4658 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4659 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4660 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4661 * Feel free to add tests for other text modes or StreamIn things.
4665 HWND hwndRichEdit = new_richedit(NULL);
4666 LRESULT result;
4667 EDITSTREAM es;
4668 char buffer[1024] = {0};
4669 const char randomtext[] = "Some text";
4671 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4673 /* StreamIn, no SFF_SELECTION */
4674 es.dwCookie = nCallbackCount;
4675 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4676 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4677 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4678 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4679 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4680 result = strcmp (buffer,"test");
4681 ok (result == 0,
4682 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4684 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4685 ok (result == FALSE,
4686 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4688 /* StreamIn, SFF_SELECTION, but nothing selected */
4689 es.dwCookie = nCallbackCount;
4690 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4691 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4692 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4693 SendMessage(hwndRichEdit, EM_STREAMIN,
4694 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4695 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4696 result = strcmp (buffer,"testSome text");
4697 ok (result == 0,
4698 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4700 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4701 ok (result == TRUE,
4702 "EM_STREAMIN with SFF_SELECTION but no selection set "
4703 "should create an undo\n");
4705 /* StreamIn, SFF_SELECTION, with a selection */
4706 es.dwCookie = nCallbackCount;
4707 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4708 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4709 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
4710 SendMessage(hwndRichEdit, EM_STREAMIN,
4711 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4712 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4713 result = strcmp (buffer,"Sometesttext");
4714 ok (result == 0,
4715 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4717 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4718 ok (result == TRUE,
4719 "EM_STREAMIN with SFF_SELECTION and selection set "
4720 "should create an undo\n");
4722 DestroyWindow(hwndRichEdit);
4725 static BOOL is_em_settextex_supported(HWND hwnd)
4727 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
4728 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
4731 static void test_unicode_conversions(void)
4733 static const WCHAR tW[] = {'t',0};
4734 static const WCHAR teW[] = {'t','e',0};
4735 static const WCHAR textW[] = {'t','e','s','t',0};
4736 static const char textA[] = "test";
4737 char bufA[64];
4738 WCHAR bufW[64];
4739 HWND hwnd;
4740 int is_win9x, em_settextex_supported, ret;
4742 is_win9x = GetVersion() & 0x80000000;
4744 #define set_textA(hwnd, wm_set_text, txt) \
4745 do { \
4746 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
4747 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4748 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4749 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4750 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
4751 } while(0)
4752 #define expect_textA(hwnd, wm_get_text, txt) \
4753 do { \
4754 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4755 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4756 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4757 memset(bufA, 0xAA, sizeof(bufA)); \
4758 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4759 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4760 ret = lstrcmpA(bufA, txt); \
4761 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
4762 } while(0)
4764 #define set_textW(hwnd, wm_set_text, txt) \
4765 do { \
4766 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
4767 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4768 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4769 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4770 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
4771 } while(0)
4772 #define expect_textW(hwnd, wm_get_text, txt) \
4773 do { \
4774 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
4775 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4776 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4777 memset(bufW, 0xAA, sizeof(bufW)); \
4778 if (is_win9x) \
4780 assert(wm_get_text == EM_GETTEXTEX); \
4781 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4782 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4784 else \
4786 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4787 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
4789 ret = lstrcmpW(bufW, txt); \
4790 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
4791 } while(0)
4792 #define expect_empty(hwnd, wm_get_text) \
4793 do { \
4794 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4795 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4796 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4797 memset(bufA, 0xAA, sizeof(bufA)); \
4798 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4799 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
4800 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
4801 } while(0)
4803 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4804 0, 0, 200, 60, 0, 0, 0, 0);
4805 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4807 ret = IsWindowUnicode(hwnd);
4808 if (is_win9x)
4809 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
4810 else
4811 ok(ret, "RichEdit20W should be unicode under NT\n");
4813 /* EM_SETTEXTEX is supported starting from version 3.0 */
4814 em_settextex_supported = is_em_settextex_supported(hwnd);
4815 trace("EM_SETTEXTEX is %ssupported on this platform\n",
4816 em_settextex_supported ? "" : "NOT ");
4818 expect_empty(hwnd, WM_GETTEXT);
4819 expect_empty(hwnd, EM_GETTEXTEX);
4821 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
4822 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4823 expect_textA(hwnd, WM_GETTEXT, "t");
4824 expect_textA(hwnd, EM_GETTEXTEX, "t");
4825 expect_textW(hwnd, EM_GETTEXTEX, tW);
4827 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
4828 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4829 expect_textA(hwnd, WM_GETTEXT, "te");
4830 expect_textA(hwnd, EM_GETTEXTEX, "te");
4831 expect_textW(hwnd, EM_GETTEXTEX, teW);
4833 set_textA(hwnd, WM_SETTEXT, NULL);
4834 expect_empty(hwnd, WM_GETTEXT);
4835 expect_empty(hwnd, EM_GETTEXTEX);
4837 if (is_win9x)
4838 set_textA(hwnd, WM_SETTEXT, textW);
4839 else
4840 set_textA(hwnd, WM_SETTEXT, textA);
4841 expect_textA(hwnd, WM_GETTEXT, textA);
4842 expect_textA(hwnd, EM_GETTEXTEX, textA);
4843 expect_textW(hwnd, EM_GETTEXTEX, textW);
4845 if (em_settextex_supported)
4847 set_textA(hwnd, EM_SETTEXTEX, textA);
4848 expect_textA(hwnd, WM_GETTEXT, textA);
4849 expect_textA(hwnd, EM_GETTEXTEX, textA);
4850 expect_textW(hwnd, EM_GETTEXTEX, textW);
4853 if (!is_win9x)
4855 set_textW(hwnd, WM_SETTEXT, textW);
4856 expect_textW(hwnd, WM_GETTEXT, textW);
4857 expect_textA(hwnd, WM_GETTEXT, textA);
4858 expect_textW(hwnd, EM_GETTEXTEX, textW);
4859 expect_textA(hwnd, EM_GETTEXTEX, textA);
4861 if (em_settextex_supported)
4863 set_textW(hwnd, EM_SETTEXTEX, textW);
4864 expect_textW(hwnd, WM_GETTEXT, textW);
4865 expect_textA(hwnd, WM_GETTEXT, textA);
4866 expect_textW(hwnd, EM_GETTEXTEX, textW);
4867 expect_textA(hwnd, EM_GETTEXTEX, textA);
4870 DestroyWindow(hwnd);
4872 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
4873 0, 0, 200, 60, 0, 0, 0, 0);
4874 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4876 ret = IsWindowUnicode(hwnd);
4877 ok(!ret, "RichEdit20A should NOT be unicode\n");
4879 set_textA(hwnd, WM_SETTEXT, textA);
4880 expect_textA(hwnd, WM_GETTEXT, textA);
4881 expect_textA(hwnd, EM_GETTEXTEX, textA);
4882 expect_textW(hwnd, EM_GETTEXTEX, textW);
4884 if (em_settextex_supported)
4886 set_textA(hwnd, EM_SETTEXTEX, textA);
4887 expect_textA(hwnd, WM_GETTEXT, textA);
4888 expect_textA(hwnd, EM_GETTEXTEX, textA);
4889 expect_textW(hwnd, EM_GETTEXTEX, textW);
4892 if (!is_win9x)
4894 set_textW(hwnd, WM_SETTEXT, textW);
4895 expect_textW(hwnd, WM_GETTEXT, textW);
4896 expect_textA(hwnd, WM_GETTEXT, textA);
4897 expect_textW(hwnd, EM_GETTEXTEX, textW);
4898 expect_textA(hwnd, EM_GETTEXTEX, textA);
4900 if (em_settextex_supported)
4902 set_textW(hwnd, EM_SETTEXTEX, textW);
4903 expect_textW(hwnd, WM_GETTEXT, textW);
4904 expect_textA(hwnd, WM_GETTEXT, textA);
4905 expect_textW(hwnd, EM_GETTEXTEX, textW);
4906 expect_textA(hwnd, EM_GETTEXTEX, textA);
4909 DestroyWindow(hwnd);
4912 static void test_WM_CHAR(void)
4914 HWND hwnd;
4915 int ret;
4916 const char * char_list = "abc\rabc\r";
4917 const char * expected_content_single = "abcabc";
4918 const char * expected_content_multi = "abc\r\nabc\r\n";
4919 char buffer[64] = {0};
4920 const char * p;
4922 /* single-line control must IGNORE carriage returns */
4923 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4924 0, 0, 200, 60, 0, 0, 0, 0);
4925 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4927 p = char_list;
4928 while (*p != '\0') {
4929 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4930 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4931 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4932 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4933 p++;
4936 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4937 ret = strcmp(buffer, expected_content_single);
4938 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4940 DestroyWindow(hwnd);
4942 /* multi-line control inserts CR normally */
4943 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4944 0, 0, 200, 60, 0, 0, 0, 0);
4945 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4947 p = char_list;
4948 while (*p != '\0') {
4949 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4950 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4951 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4952 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4953 p++;
4956 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4957 ret = strcmp(buffer, expected_content_multi);
4958 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4960 DestroyWindow(hwnd);
4963 static void test_EM_GETTEXTLENGTHEX(void)
4965 HWND hwnd;
4966 GETTEXTLENGTHEX gtl;
4967 int ret;
4968 const char * base_string = "base string";
4969 const char * test_string = "a\nb\n\n\r\n";
4970 const char * test_string_after = "a";
4971 const char * test_string_2 = "a\rtest\rstring";
4972 char buffer[64] = {0};
4974 /* single line */
4975 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4976 0, 0, 200, 60, 0, 0, 0, 0);
4977 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4979 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4980 gtl.codepage = CP_ACP;
4981 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4982 ok(ret == 0, "ret %d\n",ret);
4984 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4985 gtl.codepage = CP_ACP;
4986 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4987 ok(ret == 0, "ret %d\n",ret);
4989 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4991 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4992 gtl.codepage = CP_ACP;
4993 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4994 ok(ret == strlen(base_string), "ret %d\n",ret);
4996 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4997 gtl.codepage = CP_ACP;
4998 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4999 ok(ret == strlen(base_string), "ret %d\n",ret);
5001 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5003 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5004 gtl.codepage = CP_ACP;
5005 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5006 ok(ret == 1, "ret %d\n",ret);
5008 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5009 gtl.codepage = CP_ACP;
5010 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5011 ok(ret == 1, "ret %d\n",ret);
5013 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5014 ret = strcmp(buffer, test_string_after);
5015 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5017 DestroyWindow(hwnd);
5019 /* multi line */
5020 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5021 0, 0, 200, 60, 0, 0, 0, 0);
5022 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5024 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5025 gtl.codepage = CP_ACP;
5026 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5027 ok(ret == 0, "ret %d\n",ret);
5029 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5030 gtl.codepage = CP_ACP;
5031 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5032 ok(ret == 0, "ret %d\n",ret);
5034 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5036 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5037 gtl.codepage = CP_ACP;
5038 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5039 ok(ret == strlen(base_string), "ret %d\n",ret);
5041 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5042 gtl.codepage = CP_ACP;
5043 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5044 ok(ret == strlen(base_string), "ret %d\n",ret);
5046 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5048 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5049 gtl.codepage = CP_ACP;
5050 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5051 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5053 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5054 gtl.codepage = CP_ACP;
5055 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5056 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5058 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5060 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5061 gtl.codepage = CP_ACP;
5062 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5063 ok(ret == 10, "ret %d\n",ret);
5065 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5066 gtl.codepage = CP_ACP;
5067 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5068 ok(ret == 6, "ret %d\n",ret);
5070 DestroyWindow(hwnd);
5074 /* globals that parent and child access when checking event masks & notifications */
5075 static HWND eventMaskEditHwnd = 0;
5076 static int queriedEventMask;
5077 static int watchForEventMask = 0;
5079 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5080 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5082 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5084 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5086 return DefWindowProcA(hwnd, message, wParam, lParam);
5089 /* test event masks in combination with WM_COMMAND */
5090 static void test_eventMask(void)
5092 HWND parent;
5093 int ret;
5094 WNDCLASSA cls;
5095 const char text[] = "foo bar\n";
5096 int eventMask;
5098 /* register class to capture WM_COMMAND */
5099 cls.style = 0;
5100 cls.lpfnWndProc = ParentMsgCheckProcA;
5101 cls.cbClsExtra = 0;
5102 cls.cbWndExtra = 0;
5103 cls.hInstance = GetModuleHandleA(0);
5104 cls.hIcon = 0;
5105 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5106 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5107 cls.lpszMenuName = NULL;
5108 cls.lpszClassName = "EventMaskParentClass";
5109 if(!RegisterClassA(&cls)) assert(0);
5111 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5112 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5113 ok (parent != 0, "Failed to create parent window\n");
5115 eventMaskEditHwnd = new_richedit(parent);
5116 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5118 eventMask = ENM_CHANGE | ENM_UPDATE;
5119 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5120 ok(ret == ENM_NONE, "wrong event mask\n");
5121 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5122 ok(ret == eventMask, "failed to set event mask\n");
5124 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5125 queriedEventMask = 0; /* initialize to something other than we expect */
5126 watchForEventMask = EN_CHANGE;
5127 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5128 ok(ret == TRUE, "failed to set text\n");
5129 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5130 notification in response to WM_SETTEXT */
5131 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5132 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5136 static int received_WM_NOTIFY = 0;
5137 static int modify_at_WM_NOTIFY = 0;
5138 static HWND hwndRichedit_WM_NOTIFY;
5140 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5142 if(message == WM_NOTIFY)
5144 received_WM_NOTIFY = 1;
5145 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5147 return DefWindowProcA(hwnd, message, wParam, lParam);
5150 static void test_WM_NOTIFY(void)
5152 HWND parent;
5153 WNDCLASSA cls;
5154 CHARFORMAT2 cf2;
5156 /* register class to capture WM_NOTIFY */
5157 cls.style = 0;
5158 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5159 cls.cbClsExtra = 0;
5160 cls.cbWndExtra = 0;
5161 cls.hInstance = GetModuleHandleA(0);
5162 cls.hIcon = 0;
5163 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5164 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5165 cls.lpszMenuName = NULL;
5166 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5167 if(!RegisterClassA(&cls)) assert(0);
5169 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5170 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5171 ok (parent != 0, "Failed to create parent window\n");
5173 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5174 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5176 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5178 /* Notifications for selection change should only be sent when selection
5179 actually changes. EM_SETCHARFORMAT is one message that calls
5180 ME_CommitUndo, which should check whether message should be sent */
5181 received_WM_NOTIFY = 0;
5182 cf2.cbSize = sizeof(CHARFORMAT2);
5183 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5184 (LPARAM) &cf2);
5185 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5186 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5187 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5188 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5190 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5191 already at 0. */
5192 received_WM_NOTIFY = 0;
5193 modify_at_WM_NOTIFY = 0;
5194 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5195 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5196 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5198 received_WM_NOTIFY = 0;
5199 modify_at_WM_NOTIFY = 0;
5200 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5201 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5203 received_WM_NOTIFY = 0;
5204 modify_at_WM_NOTIFY = 0;
5205 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5206 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5207 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5209 DestroyWindow(hwndRichedit_WM_NOTIFY);
5210 DestroyWindow(parent);
5213 static void test_undo_coalescing(void)
5215 HWND hwnd;
5216 int result;
5217 char buffer[64] = {0};
5219 /* multi-line control inserts CR normally */
5220 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5221 0, 0, 200, 60, 0, 0, 0, 0);
5222 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5224 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5225 ok (result == FALSE, "Can undo after window creation.\n");
5226 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5227 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5228 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5229 ok (result == FALSE, "Can redo after window creation.\n");
5230 result = SendMessage(hwnd, EM_REDO, 0, 0);
5231 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5233 /* Test the effect of arrows keys during typing on undo transactions*/
5234 simulate_typing_characters(hwnd, "one two three");
5235 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5236 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5237 simulate_typing_characters(hwnd, " four five six");
5239 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5240 ok (result == FALSE, "Can redo before anything is undone.\n");
5241 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5242 ok (result == TRUE, "Cannot undo typed characters.\n");
5243 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5244 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5245 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5246 ok (result == TRUE, "Cannot redo after undo.\n");
5247 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5248 result = strcmp(buffer, "one two three");
5249 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5251 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5252 ok (result == TRUE, "Cannot undo typed characters.\n");
5253 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5254 ok (result == TRUE, "Failed to undo typed characters.\n");
5255 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5256 result = strcmp(buffer, "");
5257 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5259 /* Test the effect of focus changes during typing on undo transactions*/
5260 simulate_typing_characters(hwnd, "one two three");
5261 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5262 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5263 SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
5264 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
5265 simulate_typing_characters(hwnd, " four five six");
5266 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5267 ok (result == TRUE, "Failed to undo typed characters.\n");
5268 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5269 result = strcmp(buffer, "one two three");
5270 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5272 /* Test the effect of the back key during typing on undo transactions */
5273 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5274 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5275 ok (result == TRUE, "Failed to clear the text.\n");
5276 simulate_typing_characters(hwnd, "one two threa");
5277 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5278 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5279 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5280 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5281 simulate_typing_characters(hwnd, "e four five six");
5282 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5283 ok (result == TRUE, "Failed to undo typed characters.\n");
5284 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5285 result = strcmp(buffer, "");
5286 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5288 /* Test the effect of the delete key during typing on undo transactions */
5289 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5290 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5291 ok(result == TRUE, "Failed to set the text.\n");
5292 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5293 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5294 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5295 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5296 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5297 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5298 ok (result == TRUE, "Failed to undo typed characters.\n");
5299 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5300 result = strcmp(buffer, "acd");
5301 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5302 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5303 ok (result == TRUE, "Failed to undo typed characters.\n");
5304 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5305 result = strcmp(buffer, "abcd");
5306 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5308 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5309 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5310 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5311 ok (result == TRUE, "Failed to clear the text.\n");
5312 simulate_typing_characters(hwnd, "one two three");
5313 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5314 ok (result == 0, "expected %d but got %d\n", 0, result);
5315 simulate_typing_characters(hwnd, " four five six");
5316 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5317 ok (result == TRUE, "Failed to undo typed characters.\n");
5318 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5319 result = strcmp(buffer, "one two three");
5320 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5321 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5322 ok (result == TRUE, "Failed to undo typed characters.\n");
5323 ok (result == TRUE, "Failed to undo typed characters.\n");
5324 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5325 result = strcmp(buffer, "");
5326 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5328 DestroyWindow(hwnd);
5331 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5332 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5334 static void test_word_movement(void)
5336 HWND hwnd;
5337 int result;
5338 int sel_start, sel_end;
5340 /* multi-line control inserts CR normally */
5341 hwnd = new_richedit(NULL);
5343 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5344 ok (result == TRUE, "Failed to clear the text.\n");
5345 SendMessage(hwnd, EM_SETSEL, 0, 0);
5346 /* |one two three */
5348 SEND_CTRL_RIGHT(hwnd);
5349 /* one |two three */
5350 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5351 ok(sel_start == sel_end, "Selection should be empty\n");
5352 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5354 SEND_CTRL_RIGHT(hwnd);
5355 /* one two |three */
5356 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5357 ok(sel_start == sel_end, "Selection should be empty\n");
5358 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5360 SEND_CTRL_LEFT(hwnd);
5361 /* one |two three */
5362 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5363 ok(sel_start == sel_end, "Selection should be empty\n");
5364 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5366 SEND_CTRL_LEFT(hwnd);
5367 /* |one two three */
5368 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5369 ok(sel_start == sel_end, "Selection should be empty\n");
5370 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5372 SendMessage(hwnd, EM_SETSEL, 8, 8);
5373 /* one two | three */
5374 SEND_CTRL_RIGHT(hwnd);
5375 /* one two |three */
5376 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5377 ok(sel_start == sel_end, "Selection should be empty\n");
5378 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5380 SendMessage(hwnd, EM_SETSEL, 11, 11);
5381 /* one two th|ree */
5382 SEND_CTRL_LEFT(hwnd);
5383 /* one two |three */
5384 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5385 ok(sel_start == sel_end, "Selection should be empty\n");
5386 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5388 DestroyWindow(hwnd);
5391 static void test_EM_CHARFROMPOS(void)
5393 HWND hwnd;
5394 int result;
5395 POINTL point;
5396 point.x = 0;
5397 point.y = 50;
5399 /* multi-line control inserts CR normally */
5400 hwnd = new_richedit(NULL);
5401 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5402 (LPARAM)"one two three four five six seven");
5404 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5405 ok(result == 0, "expected character index of 0 but got %d\n", result);
5407 DestroyWindow(hwnd);
5410 START_TEST( editor )
5412 MSG msg;
5413 time_t end;
5415 /* Must explicitly LoadLibrary(). The test has no references to functions in
5416 * RICHED20.DLL, so the linker doesn't actually link to it. */
5417 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
5418 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
5419 test_WM_CHAR();
5420 test_EM_FINDTEXT();
5421 test_EM_GETLINE();
5422 test_EM_POSFROMCHAR();
5423 test_EM_SCROLLCARET();
5424 test_EM_SCROLL();
5425 test_scrollbar_visibility();
5426 test_WM_SETTEXT();
5427 test_EM_LINELENGTH();
5428 test_EM_SETCHARFORMAT();
5429 test_EM_SETTEXTMODE();
5430 test_TM_PLAINTEXT();
5431 test_EM_SETOPTIONS();
5432 test_WM_GETTEXT();
5433 test_EM_GETTEXTRANGE();
5434 test_EM_GETSELTEXT();
5435 test_EM_SETUNDOLIMIT();
5436 test_ES_PASSWORD();
5437 test_EM_SETTEXTEX();
5438 test_EM_LIMITTEXT();
5439 test_EM_EXLIMITTEXT();
5440 test_EM_GETLIMITTEXT();
5441 test_WM_SETFONT();
5442 test_EM_GETMODIFY();
5443 test_EM_EXSETSEL();
5444 test_WM_PASTE();
5445 test_EM_STREAMIN();
5446 test_EM_STREAMOUT();
5447 test_EM_StreamIn_Undo();
5448 test_EM_FORMATRANGE();
5449 test_unicode_conversions();
5450 test_EM_GETTEXTLENGTHEX();
5451 test_EM_REPLACESEL(1);
5452 test_EM_REPLACESEL(0);
5453 test_WM_NOTIFY();
5454 test_EM_AUTOURLDETECT();
5455 test_eventMask();
5456 test_undo_coalescing();
5457 test_word_movement();
5458 test_EM_CHARFROMPOS();
5459 test_SETPARAFORMAT();
5461 /* Set the environment variable WINETEST_RICHED20 to keep windows
5462 * responsive and open for 30 seconds. This is useful for debugging.
5464 * The message pump uses PeekMessage() to empty the queue and then sleeps for
5465 * 50ms before retrying the queue. */
5466 end = time(NULL) + 30;
5467 if (getenv( "WINETEST_RICHED20" )) {
5468 while (time(NULL) < end) {
5469 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
5470 TranslateMessage(&msg);
5471 DispatchMessage(&msg);
5472 } else {
5473 Sleep(50);
5478 OleFlushClipboard();
5479 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());