richedit: Use RTF reader for text starting with {\urtf.
[wine/hacks.git] / dlls / riched20 / tests / editor.c
blob54b100c7e56b6280440010730774653d68e705c3
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));
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 resultbuf[0] = '\0';
332 for (j = 0; j < 32; j++)
333 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
334 expectedbuf[0] = '\0';
335 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
336 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
337 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
338 sprintf(expectedbuf+strlen(expectedbuf), "??");
339 for (; j < 32; j++) /* Bytes after declared buffer size */
340 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
342 /* Test the part of the buffer that is expected to be written according
343 * to the MSDN documentation fo EM_GETLINE, which does not state that
344 * a NULL terminating character will be added unless no text is copied.
346 * Windows 95, 98 & NT do not append a NULL terminating character, but
347 * Windows 2000 and up do append a NULL terminating character if there
348 * is space in the buffer. The test will ignore this difference. */
349 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
350 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
351 i, expected_bytes_written, expectedbuf, resultbuf);
352 /* Test the part of the buffer after the declared length to make sure
353 * there are no buffer overruns. */
354 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
355 nBuf - gl[i].buffer_len),
356 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
357 i, expected_bytes_written, expectedbuf, resultbuf);
361 DestroyWindow(hwndRichEdit);
364 static void test_EM_LINELENGTH(void)
366 HWND hwndRichEdit = new_richedit(NULL);
367 const char * text =
368 "richedit1\r"
369 "richedit1\n"
370 "richedit1\r\n"
371 "richedit1";
372 int offset_test[10][2] = {
373 {0, 9},
374 {5, 9},
375 {10, 9},
376 {15, 9},
377 {20, 9},
378 {25, 9},
379 {30, 9},
380 {35, 9},
381 {40, 0},
382 {45, 0},
384 int i;
385 LRESULT result;
387 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
389 for (i = 0; i < 10; i++) {
390 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
391 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
392 offset_test[i][0], result, offset_test[i][1]);
395 DestroyWindow(hwndRichEdit);
398 static int get_scroll_pos_y(HWND hwnd)
400 POINT p = {-1, -1};
401 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
402 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
403 return p.y;
406 static void move_cursor(HWND hwnd, long charindex)
408 CHARRANGE cr;
409 cr.cpMax = charindex;
410 cr.cpMin = charindex;
411 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
414 static void line_scroll(HWND hwnd, int amount)
416 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
419 static void test_EM_SCROLLCARET(void)
421 int prevY, curY;
422 const char text[] = "aa\n"
423 "this is a long line of text that should be longer than the "
424 "control's width\n"
425 "cc\n"
426 "dd\n"
427 "ee\n"
428 "ff\n"
429 "gg\n"
430 "hh\n";
431 /* The richedit window height needs to be large enough vertically to fit in
432 * more than two lines of text, so the new_richedit function can't be used
433 * since a height of 60 was not large enough on some systems.
435 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
436 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
437 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
438 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
440 /* Can't verify this */
441 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
443 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
445 /* Caret above visible window */
446 line_scroll(hwndRichEdit, 3);
447 prevY = get_scroll_pos_y(hwndRichEdit);
448 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
449 curY = get_scroll_pos_y(hwndRichEdit);
450 ok(prevY != curY, "%d == %d\n", prevY, curY);
452 /* Caret below visible window */
453 move_cursor(hwndRichEdit, sizeof(text) - 1);
454 line_scroll(hwndRichEdit, -3);
455 prevY = get_scroll_pos_y(hwndRichEdit);
456 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
457 curY = get_scroll_pos_y(hwndRichEdit);
458 ok(prevY != curY, "%d == %d\n", prevY, curY);
460 /* Caret in visible window */
461 move_cursor(hwndRichEdit, sizeof(text) - 2);
462 prevY = get_scroll_pos_y(hwndRichEdit);
463 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
464 curY = get_scroll_pos_y(hwndRichEdit);
465 ok(prevY == curY, "%d != %d\n", prevY, curY);
467 /* Caret still in visible window */
468 line_scroll(hwndRichEdit, -1);
469 prevY = get_scroll_pos_y(hwndRichEdit);
470 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
471 curY = get_scroll_pos_y(hwndRichEdit);
472 ok(prevY == curY, "%d != %d\n", prevY, curY);
474 DestroyWindow(hwndRichEdit);
477 static void test_EM_POSFROMCHAR(void)
479 HWND hwndRichEdit = new_richedit(NULL);
480 int i;
481 LRESULT result;
482 unsigned int height = 0;
483 int xpos = 0;
484 static const char text[] = "aa\n"
485 "this is a long line of text that should be longer than the "
486 "control's width\n"
487 "cc\n"
488 "dd\n"
489 "ee\n"
490 "ff\n"
491 "gg\n"
492 "hh\n";
494 /* Fill the control to lines to ensure that most of them are offscreen */
495 for (i = 0; i < 50; i++)
497 /* Do not modify the string; it is exactly 16 characters long. */
498 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
499 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
503 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
504 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
505 Richedit 3.0 accepts either of the above API conventions.
508 /* Testing Richedit 2.0 API format */
510 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
511 Since all lines are identical and drawn with the same font,
512 they should have the same height... right?
514 for (i = 0; i < 50; i++)
516 /* All the lines are 16 characters long */
517 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
518 if (i == 0)
520 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
521 todo_wine {
522 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
524 xpos = LOWORD(result);
526 else if (i == 1)
528 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
529 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
530 height = HIWORD(result);
532 else
534 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
535 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
539 /* Testing position at end of text */
540 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
541 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
542 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
544 /* Testing position way past end of text */
545 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
546 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
547 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
549 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
550 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
551 for (i = 0; i < 50; i++)
553 /* All the lines are 16 characters long */
554 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
555 ok((signed short)(HIWORD(result)) == (i - 1) * height,
556 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
557 (signed short)(HIWORD(result)), (i - 1) * height);
558 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
561 /* Testing position at end of text */
562 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
563 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
564 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
566 /* Testing position way past end of text */
567 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
568 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
569 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
571 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
572 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
573 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
575 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
576 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
577 todo_wine {
578 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
580 xpos = LOWORD(result);
582 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
583 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
584 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
585 todo_wine {
586 /* Fails on builtin because horizontal scrollbar is not being shown */
587 ok((signed short)(LOWORD(result)) < xpos,
588 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
589 (signed short)(LOWORD(result)), xpos);
591 DestroyWindow(hwndRichEdit);
594 static void test_EM_SETCHARFORMAT(void)
596 HWND hwndRichEdit = new_richedit(NULL);
597 CHARFORMAT2 cf2;
598 int rc = 0;
599 int tested_effects[] = {
600 CFE_BOLD,
601 CFE_ITALIC,
602 CFE_UNDERLINE,
603 CFE_STRIKEOUT,
604 CFE_PROTECTED,
605 CFE_LINK,
606 CFE_SUBSCRIPT,
607 CFE_SUPERSCRIPT,
610 int i;
611 CHARRANGE cr;
613 /* Invalid flags, CHARFORMAT2 structure blanked out */
614 memset(&cf2, 0, sizeof(cf2));
615 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
616 (LPARAM) &cf2);
617 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
619 /* A valid flag, CHARFORMAT2 structure blanked out */
620 memset(&cf2, 0, sizeof(cf2));
621 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
622 (LPARAM) &cf2);
623 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
625 /* A valid flag, CHARFORMAT2 structure blanked out */
626 memset(&cf2, 0, sizeof(cf2));
627 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
628 (LPARAM) &cf2);
629 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
631 /* A valid flag, CHARFORMAT2 structure blanked out */
632 memset(&cf2, 0, sizeof(cf2));
633 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
634 (LPARAM) &cf2);
635 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
637 /* A valid flag, CHARFORMAT2 structure blanked out */
638 memset(&cf2, 0, sizeof(cf2));
639 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
640 (LPARAM) &cf2);
641 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
643 /* Invalid flags, CHARFORMAT2 structure minimally filled */
644 memset(&cf2, 0, sizeof(cf2));
645 cf2.cbSize = sizeof(CHARFORMAT2);
646 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
647 (LPARAM) &cf2);
648 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
649 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
650 ok(rc == FALSE, "Should not be able to undo here.\n");
651 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
653 /* A valid flag, CHARFORMAT2 structure minimally filled */
654 memset(&cf2, 0, sizeof(cf2));
655 cf2.cbSize = sizeof(CHARFORMAT2);
656 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
657 (LPARAM) &cf2);
658 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
659 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
660 ok(rc == FALSE, "Should not be able to undo here.\n");
661 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
663 /* A valid flag, CHARFORMAT2 structure minimally filled */
664 memset(&cf2, 0, sizeof(cf2));
665 cf2.cbSize = sizeof(CHARFORMAT2);
666 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
667 (LPARAM) &cf2);
668 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
669 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
670 ok(rc == FALSE, "Should not be able to undo here.\n");
671 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
673 /* A valid flag, CHARFORMAT2 structure minimally filled */
674 memset(&cf2, 0, sizeof(cf2));
675 cf2.cbSize = sizeof(CHARFORMAT2);
676 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
677 (LPARAM) &cf2);
678 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
679 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
680 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
681 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
683 /* A valid flag, CHARFORMAT2 structure minimally filled */
684 memset(&cf2, 0, sizeof(cf2));
685 cf2.cbSize = sizeof(CHARFORMAT2);
686 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
687 (LPARAM) &cf2);
688 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
689 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
690 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
691 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
693 cf2.cbSize = sizeof(CHARFORMAT2);
694 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
695 (LPARAM) &cf2);
697 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
698 cf2.cbSize = sizeof(CHARFORMAT2);
699 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
700 (LPARAM) &cf2);
701 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
702 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
704 /* wParam==0 is default char format, does not set modify */
705 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
706 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
707 ok(rc == 0, "Text marked as modified, expected not modified!\n");
708 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
709 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
710 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
711 ok(rc == 0, "Text marked as modified, expected not modified!\n");
713 /* wParam==SCF_SELECTION sets modify if nonempty selection */
714 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
715 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
716 ok(rc == 0, "Text marked as modified, expected not modified!\n");
717 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
718 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
719 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
720 ok(rc == 0, "Text marked as modified, expected not modified!\n");
722 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
723 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
724 ok(rc == 0, "Text marked as modified, expected not modified!\n");
725 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
726 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
727 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
728 ok(rc == 0, "Text marked as modified, expected not modified!\n");
729 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
730 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
731 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
732 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
733 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
735 /* wParam==SCF_ALL sets modify regardless of whether text is present */
736 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
737 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
738 ok(rc == 0, "Text marked as modified, expected not modified!\n");
739 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
740 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
741 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
742 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
744 DestroyWindow(hwndRichEdit);
746 /* EM_GETCHARFORMAT tests */
747 for (i = 0; tested_effects[i]; i++)
749 hwndRichEdit = new_richedit(NULL);
750 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
752 /* Need to set a TrueType font to get consistent CFM_BOLD results */
753 memset(&cf2, 0, sizeof(CHARFORMAT2));
754 cf2.cbSize = sizeof(CHARFORMAT2);
755 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
756 cf2.dwEffects = 0;
757 strcpy(cf2.szFaceName, "Courier New");
758 cf2.wWeight = FW_DONTCARE;
759 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
761 memset(&cf2, 0, sizeof(CHARFORMAT2));
762 cf2.cbSize = sizeof(CHARFORMAT2);
763 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
764 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
765 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
766 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
768 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
769 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
770 ok((cf2.dwEffects & tested_effects[i]) == 0,
771 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
773 memset(&cf2, 0, sizeof(CHARFORMAT2));
774 cf2.cbSize = sizeof(CHARFORMAT2);
775 cf2.dwMask = tested_effects[i];
776 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
777 cf2.dwMask = CFM_SUPERSCRIPT;
778 cf2.dwEffects = tested_effects[i];
779 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
780 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
782 memset(&cf2, 0, sizeof(CHARFORMAT2));
783 cf2.cbSize = sizeof(CHARFORMAT2);
784 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
785 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
786 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
787 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
789 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
790 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
791 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
792 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
794 memset(&cf2, 0, sizeof(CHARFORMAT2));
795 cf2.cbSize = sizeof(CHARFORMAT2);
796 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
797 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
798 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
799 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
801 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
802 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
803 ok((cf2.dwEffects & tested_effects[i]) == 0,
804 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
806 memset(&cf2, 0, sizeof(CHARFORMAT2));
807 cf2.cbSize = sizeof(CHARFORMAT2);
808 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
809 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
810 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
811 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
813 (cf2.dwMask & tested_effects[i]) == 0),
814 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
816 DestroyWindow(hwndRichEdit);
819 for (i = 0; tested_effects[i]; i++)
821 hwndRichEdit = new_richedit(NULL);
822 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
824 /* Need to set a TrueType font to get consistent CFM_BOLD results */
825 memset(&cf2, 0, sizeof(CHARFORMAT2));
826 cf2.cbSize = sizeof(CHARFORMAT2);
827 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
828 cf2.dwEffects = 0;
829 strcpy(cf2.szFaceName, "Courier New");
830 cf2.wWeight = FW_DONTCARE;
831 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
833 memset(&cf2, 0, sizeof(CHARFORMAT2));
834 cf2.cbSize = sizeof(CHARFORMAT2);
835 cf2.dwMask = tested_effects[i];
836 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
837 cf2.dwMask = CFM_SUPERSCRIPT;
838 cf2.dwEffects = tested_effects[i];
839 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
840 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
842 memset(&cf2, 0, sizeof(CHARFORMAT2));
843 cf2.cbSize = sizeof(CHARFORMAT2);
844 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
845 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
846 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
847 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
849 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
850 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
851 ok((cf2.dwEffects & tested_effects[i]) == 0,
852 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
854 memset(&cf2, 0, sizeof(CHARFORMAT2));
855 cf2.cbSize = sizeof(CHARFORMAT2);
856 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
857 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
858 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
859 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
861 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
862 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
863 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
864 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
866 memset(&cf2, 0, sizeof(CHARFORMAT2));
867 cf2.cbSize = sizeof(CHARFORMAT2);
868 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
869 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
870 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
871 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
873 (cf2.dwMask & tested_effects[i]) == 0),
874 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
875 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
876 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
878 DestroyWindow(hwndRichEdit);
881 /* Effects applied on an empty selection should take effect when selection is
882 replaced with text */
883 hwndRichEdit = new_richedit(NULL);
884 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
885 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
887 memset(&cf2, 0, sizeof(CHARFORMAT2));
888 cf2.cbSize = sizeof(CHARFORMAT2);
889 cf2.dwMask = CFM_BOLD;
890 cf2.dwEffects = CFE_BOLD;
891 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
893 /* Selection is now nonempty */
894 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
896 memset(&cf2, 0, sizeof(CHARFORMAT2));
897 cf2.cbSize = sizeof(CHARFORMAT2);
898 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
899 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
901 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
902 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
903 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
904 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
907 /* Set two effects on an empty selection */
908 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
909 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
911 memset(&cf2, 0, sizeof(CHARFORMAT2));
912 cf2.cbSize = sizeof(CHARFORMAT2);
913 cf2.dwMask = CFM_BOLD;
914 cf2.dwEffects = CFE_BOLD;
915 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
916 cf2.dwMask = CFM_ITALIC;
917 cf2.dwEffects = CFE_ITALIC;
918 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
920 /* Selection is now nonempty */
921 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
923 memset(&cf2, 0, sizeof(CHARFORMAT2));
924 cf2.cbSize = sizeof(CHARFORMAT2);
925 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
926 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
928 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
929 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
930 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
931 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
933 /* Setting the (empty) selection to exactly the same place as before should
934 NOT clear the insertion style! */
935 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
936 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
938 memset(&cf2, 0, sizeof(CHARFORMAT2));
939 cf2.cbSize = sizeof(CHARFORMAT2);
940 cf2.dwMask = CFM_BOLD;
941 cf2.dwEffects = CFE_BOLD;
942 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
944 /* Empty selection in same place, insert style should NOT be forgotten here. */
945 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
947 /* Selection is now nonempty */
948 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
950 memset(&cf2, 0, sizeof(CHARFORMAT2));
951 cf2.cbSize = sizeof(CHARFORMAT2);
952 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
953 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
955 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
956 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
957 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
958 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
960 /* Ditto with EM_EXSETSEL */
961 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
962 cr.cpMin = 2; cr.cpMax = 2;
963 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
965 memset(&cf2, 0, sizeof(CHARFORMAT2));
966 cf2.cbSize = sizeof(CHARFORMAT2);
967 cf2.dwMask = CFM_BOLD;
968 cf2.dwEffects = CFE_BOLD;
969 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
971 /* Empty selection in same place, insert style should NOT be forgotten here. */
972 cr.cpMin = 2; cr.cpMax = 2;
973 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
975 /* Selection is now nonempty */
976 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
978 memset(&cf2, 0, sizeof(CHARFORMAT2));
979 cf2.cbSize = sizeof(CHARFORMAT2);
980 cr.cpMin = 2; cr.cpMax = 6;
981 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
982 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
984 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
985 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
986 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
987 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
989 DestroyWindow(hwndRichEdit);
992 static void test_EM_SETTEXTMODE(void)
994 HWND hwndRichEdit = new_richedit(NULL);
995 CHARFORMAT2 cf2, cf2test;
996 CHARRANGE cr;
997 int rc = 0;
999 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1000 /*Insert text into the control*/
1002 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1004 /*Attempt to change the control to plain text mode*/
1005 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1006 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1008 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1009 If rich text is pasted, it should have the same formatting as the rest
1010 of the text in the control*/
1012 /*Italicize the text
1013 *NOTE: If the default text was already italicized, the test will simply
1014 reverse; in other words, it will copy a regular "wine" into a plain
1015 text window that uses an italicized format*/
1016 cf2.cbSize = sizeof(CHARFORMAT2);
1017 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1018 (LPARAM) &cf2);
1020 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1021 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1023 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1024 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1026 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1027 however, SCF_ALL has been implemented*/
1028 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1029 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1031 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1032 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1034 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1036 /*Select the string "wine"*/
1037 cr.cpMin = 0;
1038 cr.cpMax = 4;
1039 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1041 /*Copy the italicized "wine" to the clipboard*/
1042 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1044 /*Reset the formatting to default*/
1045 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1046 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1047 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1049 /*Clear the text in the control*/
1050 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1052 /*Switch to Plain Text Mode*/
1053 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1054 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1056 /*Input "wine" again in normal format*/
1057 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1059 /*Paste the italicized "wine" into the control*/
1060 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1062 /*Select a character from the first "wine" string*/
1063 cr.cpMin = 2;
1064 cr.cpMax = 3;
1065 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1067 /*Retrieve its formatting*/
1068 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1069 (LPARAM) &cf2);
1071 /*Select a character from the second "wine" string*/
1072 cr.cpMin = 5;
1073 cr.cpMax = 6;
1074 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1076 /*Retrieve its formatting*/
1077 cf2test.cbSize = sizeof(CHARFORMAT2);
1078 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1079 (LPARAM) &cf2test);
1081 /*Compare the two formattings*/
1082 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1083 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1084 cf2.dwEffects, cf2test.dwEffects);
1085 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1086 printing "wine" in the current format(normal)
1087 pasting "wine" from the clipboard(italicized)
1088 comparing the two formats(should differ)*/
1090 /*Attempt to switch with text in control*/
1091 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1092 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1094 /*Clear control*/
1095 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1097 /*Switch into Rich Text mode*/
1098 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1099 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1101 /*Print "wine" in normal formatting into the control*/
1102 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1104 /*Paste italicized "wine" into the control*/
1105 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1107 /*Select text from the first "wine" string*/
1108 cr.cpMin = 1;
1109 cr.cpMax = 3;
1110 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1112 /*Retrieve its formatting*/
1113 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1114 (LPARAM) &cf2);
1116 /*Select text from the second "wine" string*/
1117 cr.cpMin = 6;
1118 cr.cpMax = 7;
1119 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1121 /*Retrieve its formatting*/
1122 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1123 (LPARAM) &cf2test);
1125 /*Test that the two formattings are not the same*/
1126 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1127 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1128 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1130 DestroyWindow(hwndRichEdit);
1133 static void test_SETPARAFORMAT(void)
1135 HWND hwndRichEdit = new_richedit(NULL);
1136 PARAFORMAT2 fmt;
1137 HRESULT ret;
1138 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1139 fmt.cbSize = sizeof(PARAFORMAT2);
1140 fmt.dwMask = PFM_ALIGNMENT;
1141 fmt.wAlignment = PFA_LEFT;
1143 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1144 ok(ret != 0, "expected non-zero got %d\n", ret);
1146 fmt.cbSize = sizeof(PARAFORMAT2);
1147 fmt.dwMask = -1;
1148 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1149 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1150 * between richedit different native builds of riched20.dll
1151 * used on different Windows versions. */
1152 ret &= ~PFM_TABLEROWDELIMITER;
1153 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1155 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1156 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1158 DestroyWindow(hwndRichEdit);
1161 static void test_TM_PLAINTEXT(void)
1163 /*Tests plain text properties*/
1165 HWND hwndRichEdit = new_richedit(NULL);
1166 CHARFORMAT2 cf2, cf2test;
1167 CHARRANGE cr;
1168 int rc = 0;
1170 /*Switch to plain text mode*/
1172 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1173 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1175 /*Fill control with text*/
1177 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1179 /*Select some text and bold it*/
1181 cr.cpMin = 10;
1182 cr.cpMax = 20;
1183 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1184 cf2.cbSize = sizeof(CHARFORMAT2);
1185 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1186 (LPARAM) &cf2);
1188 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1189 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1191 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1192 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1194 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1195 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1197 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1198 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1200 /*Get the formatting of those characters*/
1202 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1204 /*Get the formatting of some other characters*/
1205 cf2test.cbSize = sizeof(CHARFORMAT2);
1206 cr.cpMin = 21;
1207 cr.cpMax = 30;
1208 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1209 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1211 /*Test that they are the same as plain text allows only one formatting*/
1213 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1214 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1215 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1217 /*Fill the control with a "wine" string, which when inserted will be bold*/
1219 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1221 /*Copy the bolded "wine" string*/
1223 cr.cpMin = 0;
1224 cr.cpMax = 4;
1225 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1226 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1228 /*Swap back to rich text*/
1230 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1231 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1233 /*Set the default formatting to bold italics*/
1235 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1236 cf2.dwMask |= CFM_ITALIC;
1237 cf2.dwEffects ^= CFE_ITALIC;
1238 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1239 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1241 /*Set the text in the control to "wine", which will be bold and italicized*/
1243 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1245 /*Paste the plain text "wine" string, which should take the insert
1246 formatting, which at the moment is bold italics*/
1248 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1250 /*Select the first "wine" string and retrieve its formatting*/
1252 cr.cpMin = 1;
1253 cr.cpMax = 3;
1254 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1255 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1257 /*Select the second "wine" string and retrieve its formatting*/
1259 cr.cpMin = 5;
1260 cr.cpMax = 7;
1261 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1262 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1264 /*Compare the two formattings. They should be the same.*/
1266 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1267 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1268 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1269 DestroyWindow(hwndRichEdit);
1272 static void test_WM_GETTEXT(void)
1274 HWND hwndRichEdit = new_richedit(NULL);
1275 static const char text[] = "Hello. My name is RichEdit!";
1276 static const char text2[] = "Hello. My name is RichEdit!\r";
1277 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1278 char buffer[1024] = {0};
1279 int result;
1281 /* Baseline test with normal-sized buffer */
1282 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1283 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1284 ok(result == lstrlen(buffer),
1285 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1286 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1287 result = strcmp(buffer,text);
1288 ok(result == 0,
1289 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1291 /* Test for returned value of WM_GETTEXTLENGTH */
1292 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1293 ok(result == lstrlen(text),
1294 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1295 result, lstrlen(text));
1297 /* Test for behavior in overflow case */
1298 memset(buffer, 0, 1024);
1299 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1300 ok(result == 0 ||
1301 result == lstrlenA(text) - 1, /* XP, win2k3 */
1302 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1303 result = strcmp(buffer,text);
1304 if (result)
1305 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1306 ok(result == 0,
1307 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1309 /* Baseline test with normal-sized buffer and carriage return */
1310 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1311 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1312 ok(result == lstrlen(buffer),
1313 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1314 result = strcmp(buffer,text2_after);
1315 ok(result == 0,
1316 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1318 /* Test for returned value of WM_GETTEXTLENGTH */
1319 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1320 ok(result == lstrlen(text2_after),
1321 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1322 result, lstrlen(text2_after));
1324 /* Test for behavior of CRLF conversion in case of overflow */
1325 memset(buffer, 0, 1024);
1326 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1327 ok(result == 0 ||
1328 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1329 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1330 result = strcmp(buffer,text2);
1331 if (result)
1332 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1333 ok(result == 0,
1334 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1336 DestroyWindow(hwndRichEdit);
1339 static void test_EM_GETTEXTRANGE(void)
1341 HWND hwndRichEdit = new_richedit(NULL);
1342 const char * text1 = "foo bar\r\nfoo bar";
1343 const char * text2 = "foo bar\rfoo bar";
1344 const char * expect = "bar\rfoo";
1345 char buffer[1024] = {0};
1346 LRESULT result;
1347 TEXTRANGEA textRange;
1349 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1351 textRange.lpstrText = buffer;
1352 textRange.chrg.cpMin = 4;
1353 textRange.chrg.cpMax = 11;
1354 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1355 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1356 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1358 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1360 textRange.lpstrText = buffer;
1361 textRange.chrg.cpMin = 4;
1362 textRange.chrg.cpMax = 11;
1363 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1364 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1365 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1367 DestroyWindow(hwndRichEdit);
1370 static void test_EM_GETSELTEXT(void)
1372 HWND hwndRichEdit = new_richedit(NULL);
1373 const char * text1 = "foo bar\r\nfoo bar";
1374 const char * text2 = "foo bar\rfoo bar";
1375 const char * expect = "bar\rfoo";
1376 char buffer[1024] = {0};
1377 LRESULT result;
1379 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1381 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1382 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1383 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1384 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1386 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1388 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1389 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1390 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1391 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1393 DestroyWindow(hwndRichEdit);
1396 /* FIXME: need to test unimplemented options and robustly test wparam */
1397 static void test_EM_SETOPTIONS(void)
1399 HWND hwndRichEdit = new_richedit(NULL);
1400 static const char text[] = "Hello. My name is RichEdit!";
1401 char buffer[1024] = {0};
1403 /* NEGATIVE TESTING - NO OPTIONS SET */
1404 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1405 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1407 /* testing no readonly by sending 'a' to the control*/
1408 SetFocus(hwndRichEdit);
1409 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1410 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1411 ok(buffer[0]=='a',
1412 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1413 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1415 /* READONLY - sending 'a' to the control */
1416 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1417 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1418 SetFocus(hwndRichEdit);
1419 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1420 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1421 ok(buffer[0]==text[0],
1422 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1424 DestroyWindow(hwndRichEdit);
1427 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1429 CHARFORMAT2W text_format;
1430 text_format.cbSize = sizeof(text_format);
1431 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1432 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1433 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1436 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1438 int link_present = 0;
1440 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1441 if (is_url)
1442 { /* control text is url; should get CFE_LINK */
1443 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1445 else
1447 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1451 static HWND new_static_wnd(HWND parent) {
1452 return new_window("Static", 0, parent);
1455 static void test_EM_AUTOURLDETECT(void)
1457 /* DO NOT change the properties of the first two elements. To shorten the
1458 tests, all tests after WM_SETTEXT test just the first two elements -
1459 one non-URL and one URL */
1460 struct urls_s {
1461 const char *text;
1462 int is_url;
1463 } urls[12] = {
1464 {"winehq.org", 0},
1465 {"http://www.winehq.org", 1},
1466 {"http//winehq.org", 0},
1467 {"ww.winehq.org", 0},
1468 {"www.winehq.org", 1},
1469 {"ftp://192.168.1.1", 1},
1470 {"ftp//192.168.1.1", 0},
1471 {"mailto:your@email.com", 1},
1472 {"prospero:prosperoserver", 1},
1473 {"telnet:test", 1},
1474 {"news:newserver", 1},
1475 {"wais:waisserver", 1}
1478 int i, j;
1479 int urlRet=-1;
1480 HWND hwndRichEdit, parent;
1482 /* All of the following should cause the URL to be detected */
1483 const char * templates_delim[] = {
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\r on it",
1487 "This is some text with ---X--- on it",
1488 "This is some text with \"X\" on it",
1489 "This is some text with 'X' on it",
1490 "This is some text with 'X' on it",
1491 "This is some text with :X: on it",
1493 "This text ends with X",
1495 "This is some text with X) on it",
1496 "This is some text with X--- on it",
1497 "This is some text with X\" on it",
1498 "This is some text with X' on it",
1499 "This is some text with X: on it",
1501 "This is some text with (X on it",
1502 "This is some text with \rX 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",
1508 /* None of these should cause the URL to be detected */
1509 const char * templates_non_delim[] = {
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",
1515 "This is some text with #X# on it",
1516 "This is some text with @X@ on it",
1517 "This is some text with \\X\\ on it",
1518 "This is some text with |X on it",
1519 "This is some text with *X on it",
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",
1527 /* All of these cause the URL detection to be extended by one more byte,
1528 thus demonstrating that the tested character is considered as part
1529 of the URL. */
1530 const char * templates_xten_delim[] = {
1531 "This is some text with X| on it",
1532 "This is some text with X* on it",
1533 "This is some text with X/ on it",
1534 "This is some text with X+ on it",
1535 "This is some text with X% on it",
1536 "This is some text with X# on it",
1537 "This is some text with X@ on it",
1538 "This is some text with X\\ on it",
1540 char buffer[1024];
1542 parent = new_static_wnd(NULL);
1543 hwndRichEdit = new_richedit(parent);
1544 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1545 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1546 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1547 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1548 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1549 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1550 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1551 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1552 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1553 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1554 /* for each url, check the text to see if CFE_LINK effect is present */
1555 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1557 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1558 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1559 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1561 /* Link detection should happen immediately upon WM_SETTEXT */
1562 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1563 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1564 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1566 DestroyWindow(hwndRichEdit);
1568 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1569 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1570 hwndRichEdit = new_richedit(parent);
1572 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1573 char * at_pos;
1574 int at_offset;
1575 int end_offset;
1577 at_pos = strchr(templates_delim[j], 'X');
1578 at_offset = at_pos - templates_delim[j];
1579 strncpy(buffer, templates_delim[j], at_offset);
1580 buffer[at_offset] = '\0';
1581 strcat(buffer, urls[i].text);
1582 strcat(buffer, templates_delim[j] + at_offset + 1);
1583 end_offset = at_offset + strlen(urls[i].text);
1585 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1586 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1588 /* This assumes no templates start with the URL itself, and that they
1589 have at least two characters before the URL text */
1590 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1591 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1592 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1593 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1594 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1595 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1597 if (urls[i].is_url)
1599 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1600 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1601 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1602 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1604 else
1606 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1607 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1608 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1609 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1611 if (buffer[end_offset] != '\0')
1613 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1614 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1615 if (buffer[end_offset +1] != '\0')
1617 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1618 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1623 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1624 char * at_pos;
1625 int at_offset;
1626 int end_offset;
1628 at_pos = strchr(templates_non_delim[j], 'X');
1629 at_offset = at_pos - templates_non_delim[j];
1630 strncpy(buffer, templates_non_delim[j], at_offset);
1631 buffer[at_offset] = '\0';
1632 strcat(buffer, urls[i].text);
1633 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1634 end_offset = at_offset + strlen(urls[i].text);
1636 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1637 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1639 /* This assumes no templates start with the URL itself, and that they
1640 have at least two characters before the URL text */
1641 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1642 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1643 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1644 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1645 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1646 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1648 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1649 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1650 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1651 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1652 if (buffer[end_offset] != '\0')
1654 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1655 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1656 if (buffer[end_offset +1] != '\0')
1658 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1659 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1664 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1665 char * at_pos;
1666 int at_offset;
1667 int end_offset;
1669 at_pos = strchr(templates_xten_delim[j], 'X');
1670 at_offset = at_pos - templates_xten_delim[j];
1671 strncpy(buffer, templates_xten_delim[j], at_offset);
1672 buffer[at_offset] = '\0';
1673 strcat(buffer, urls[i].text);
1674 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1675 end_offset = at_offset + strlen(urls[i].text);
1677 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1678 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1680 /* This assumes no templates start with the URL itself, and that they
1681 have at least two characters before the URL text */
1682 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1683 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1684 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1685 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1686 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1687 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1689 if (urls[i].is_url)
1691 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1692 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1693 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1694 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1695 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1696 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1698 else
1700 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1701 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1702 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1703 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1704 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1705 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1707 if (buffer[end_offset +1] != '\0')
1709 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1710 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1711 if (buffer[end_offset +2] != '\0')
1713 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1714 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1719 DestroyWindow(hwndRichEdit);
1720 hwndRichEdit = NULL;
1723 /* Test detection of URLs within normal text - WM_CHAR case. */
1724 /* Test only the first two URL examples for brevity */
1725 for (i = 0; i < 2; i++) {
1726 hwndRichEdit = new_richedit(parent);
1728 /* Also for brevity, test only the first three delimiters */
1729 for (j = 0; j < 3; j++) {
1730 char * at_pos;
1731 int at_offset;
1732 int end_offset;
1733 int u, v;
1735 at_pos = strchr(templates_delim[j], 'X');
1736 at_offset = at_pos - templates_delim[j];
1737 end_offset = at_offset + strlen(urls[i].text);
1739 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1740 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1741 for (u = 0; templates_delim[j][u]; u++) {
1742 if (templates_delim[j][u] == '\r') {
1743 simulate_typing_characters(hwndRichEdit, "\r");
1744 } else if (templates_delim[j][u] != 'X') {
1745 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1746 } else {
1747 for (v = 0; urls[i].text[v]; v++) {
1748 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1752 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1754 /* This assumes no templates start with the URL itself, and that they
1755 have at least two characters before the URL text */
1756 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1757 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1758 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1759 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1760 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1761 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1763 if (urls[i].is_url)
1765 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1766 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1767 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1768 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1770 else
1772 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1773 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1774 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1775 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1777 if (buffer[end_offset] != '\0')
1779 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1780 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1781 if (buffer[end_offset +1] != '\0')
1783 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1784 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1788 /* The following will insert a paragraph break after the first character
1789 of the URL candidate, thus breaking the URL. It is expected that the
1790 CFE_LINK attribute should break across both pieces of the URL */
1791 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1792 simulate_typing_characters(hwndRichEdit, "\r");
1793 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1795 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1796 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1797 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1798 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1799 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1800 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1802 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1803 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1804 /* end_offset moved because of paragraph break */
1805 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1806 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1807 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1808 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1810 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1811 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1812 if (buffer[end_offset +2] != '\0')
1814 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1815 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1819 /* The following will remove the just-inserted paragraph break, thus
1820 restoring the URL */
1821 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1822 simulate_typing_characters(hwndRichEdit, "\b");
1823 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1825 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1826 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1827 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1828 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1829 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1830 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1832 if (urls[i].is_url)
1834 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1835 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1836 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1837 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1839 else
1841 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1842 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1843 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1844 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1846 if (buffer[end_offset] != '\0')
1848 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1849 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1850 if (buffer[end_offset +1] != '\0')
1852 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1853 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1857 DestroyWindow(hwndRichEdit);
1858 hwndRichEdit = NULL;
1861 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1862 /* Test just the first two URL examples for brevity */
1863 for (i = 0; i < 2; i++) {
1864 SETTEXTEX st;
1866 hwndRichEdit = new_richedit(parent);
1868 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1869 be detected:
1870 1) Set entire text, a la WM_SETTEXT
1871 2) Set a selection of the text to the URL
1872 3) Set a portion of the text at a time, which eventually results in
1873 an URL
1874 All of them should give equivalent results
1877 /* Set entire text in one go, like WM_SETTEXT */
1878 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1879 char * at_pos;
1880 int at_offset;
1881 int end_offset;
1883 st.codepage = CP_ACP;
1884 st.flags = ST_DEFAULT;
1886 at_pos = strchr(templates_delim[j], 'X');
1887 at_offset = at_pos - templates_delim[j];
1888 strncpy(buffer, templates_delim[j], at_offset);
1889 buffer[at_offset] = '\0';
1890 strcat(buffer, urls[i].text);
1891 strcat(buffer, templates_delim[j] + at_offset + 1);
1892 end_offset = at_offset + strlen(urls[i].text);
1894 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1895 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1897 /* This assumes no templates start with the URL itself, and that they
1898 have at least two characters before the URL text */
1899 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1900 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1901 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1902 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1903 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1904 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1906 if (urls[i].is_url)
1908 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1909 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1910 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1911 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1913 else
1915 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1916 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1917 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1918 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1920 if (buffer[end_offset] != '\0')
1922 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1923 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1924 if (buffer[end_offset +1] != '\0')
1926 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1927 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1932 /* Set selection with X to the URL */
1933 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1934 char * at_pos;
1935 int at_offset;
1936 int end_offset;
1938 at_pos = strchr(templates_delim[j], 'X');
1939 at_offset = at_pos - templates_delim[j];
1940 end_offset = at_offset + strlen(urls[i].text);
1942 st.codepage = CP_ACP;
1943 st.flags = ST_DEFAULT;
1944 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1945 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1946 st.flags = ST_SELECTION;
1947 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1948 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1949 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1951 /* This assumes no templates start with the URL itself, and that they
1952 have at least two characters before the URL text */
1953 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1954 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1955 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1956 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1957 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1958 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1960 if (urls[i].is_url)
1962 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1963 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1964 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1965 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1967 else
1969 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1970 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1971 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1972 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1974 if (buffer[end_offset] != '\0')
1976 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1977 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1978 if (buffer[end_offset +1] != '\0')
1980 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1981 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1986 /* Set selection with X to the first character of the URL, then the rest */
1987 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1988 char * at_pos;
1989 int at_offset;
1990 int end_offset;
1992 at_pos = strchr(templates_delim[j], 'X');
1993 at_offset = at_pos - templates_delim[j];
1994 end_offset = at_offset + strlen(urls[i].text);
1996 strcpy(buffer, "YY");
1997 buffer[0] = urls[i].text[0];
1999 st.codepage = CP_ACP;
2000 st.flags = ST_DEFAULT;
2001 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2002 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2003 st.flags = ST_SELECTION;
2004 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2005 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2006 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2007 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2008 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2010 /* This assumes no templates start with the URL itself, and that they
2011 have at least two characters before the URL text */
2012 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2013 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2014 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2015 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2016 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2017 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2019 if (urls[i].is_url)
2021 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2022 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2023 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2024 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2026 else
2028 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2029 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2030 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2031 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2033 if (buffer[end_offset] != '\0')
2035 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2036 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2037 if (buffer[end_offset +1] != '\0')
2039 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2040 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2045 DestroyWindow(hwndRichEdit);
2046 hwndRichEdit = NULL;
2049 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2050 /* Test just the first two URL examples for brevity */
2051 for (i = 0; i < 2; i++) {
2052 hwndRichEdit = new_richedit(parent);
2054 /* Set selection with X to the URL */
2055 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2056 char * at_pos;
2057 int at_offset;
2058 int end_offset;
2060 at_pos = strchr(templates_delim[j], 'X');
2061 at_offset = at_pos - templates_delim[j];
2062 end_offset = at_offset + strlen(urls[i].text);
2064 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2065 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2066 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2067 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2068 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2070 /* This assumes no templates start with the URL itself, and that they
2071 have at least two characters before the URL text */
2072 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2073 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2074 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2075 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2076 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2077 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2079 if (urls[i].is_url)
2081 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2082 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2083 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2084 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2086 else
2088 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2089 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2090 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2091 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2093 if (buffer[end_offset] != '\0')
2095 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2096 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2097 if (buffer[end_offset +1] != '\0')
2099 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2100 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2105 /* Set selection with X to the first character of the URL, then the rest */
2106 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2107 char * at_pos;
2108 int at_offset;
2109 int end_offset;
2111 at_pos = strchr(templates_delim[j], 'X');
2112 at_offset = at_pos - templates_delim[j];
2113 end_offset = at_offset + strlen(urls[i].text);
2115 strcpy(buffer, "YY");
2116 buffer[0] = urls[i].text[0];
2118 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2119 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2120 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2121 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2122 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2123 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2124 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2126 /* This assumes no templates start with the URL itself, and that they
2127 have at least two characters before the URL text */
2128 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2129 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2130 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2131 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2132 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2133 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2135 if (urls[i].is_url)
2137 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2138 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2139 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2140 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2142 else
2144 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2145 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2146 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2147 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2149 if (buffer[end_offset] != '\0')
2151 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2152 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2153 if (buffer[end_offset +1] != '\0')
2155 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2156 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2161 DestroyWindow(hwndRichEdit);
2162 hwndRichEdit = NULL;
2165 DestroyWindow(parent);
2168 static void test_EM_SCROLL(void)
2170 int i, j;
2171 int r; /* return value */
2172 int expr; /* expected return value */
2173 HWND hwndRichEdit = new_richedit(NULL);
2174 int y_before, y_after; /* units of lines of text */
2176 /* test a richedit box containing a single line of text */
2177 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2178 expr = 0x00010000;
2179 for (i = 0; i < 4; i++) {
2180 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2182 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2183 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2184 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2185 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2186 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2187 "(i == %d)\n", y_after, i);
2191 * test a richedit box that will scroll. There are two general
2192 * cases: the case without any long lines and the case with a long
2193 * line.
2195 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2196 if (i == 0)
2197 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2198 else
2199 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2200 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2201 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2202 "LONG LINE \nb\nc\nd\ne");
2203 for (j = 0; j < 12; j++) /* reset scroll position to top */
2204 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2206 /* get first visible line */
2207 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2208 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2210 /* get new current first visible line */
2211 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2213 ok(((r & 0xffffff00) == 0x00010000) &&
2214 ((r & 0x000000ff) != 0x00000000),
2215 "EM_SCROLL page down didn't scroll by a small positive number of "
2216 "lines (r == 0x%08x)\n", r);
2217 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2218 "(line %d scrolled to line %d\n", y_before, y_after);
2220 y_before = y_after;
2222 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2223 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2224 ok(((r & 0xffffff00) == 0x0001ff00),
2225 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2226 "(r == 0x%08x)\n", r);
2227 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2228 "%d scrolled to line %d\n", y_before, y_after);
2230 y_before = y_after;
2232 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2234 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2236 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2237 "(r == 0x%08x)\n", r);
2238 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2239 "1 line (%d scrolled to %d)\n", y_before, y_after);
2241 y_before = y_after;
2243 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2245 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2247 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2248 "(r == 0x%08x)\n", r);
2249 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2250 "line (%d scrolled to %d)\n", y_before, y_after);
2252 y_before = y_after;
2254 r = SendMessage(hwndRichEdit, EM_SCROLL,
2255 SB_LINEUP, 0); /* lineup beyond top */
2257 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2259 ok(r == 0x00010000,
2260 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2261 ok(y_before == y_after,
2262 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2264 y_before = y_after;
2266 r = SendMessage(hwndRichEdit, EM_SCROLL,
2267 SB_PAGEUP, 0);/*page up beyond top */
2269 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2271 ok(r == 0x00010000,
2272 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2273 ok(y_before == y_after,
2274 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2276 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2277 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2278 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2279 r = SendMessage(hwndRichEdit, EM_SCROLL,
2280 SB_PAGEDOWN, 0); /* page down beyond bot */
2281 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2283 ok(r == 0x00010000,
2284 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2285 ok(y_before == y_after,
2286 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2287 y_before, y_after);
2289 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2290 SendMessage(hwndRichEdit, EM_SCROLL,
2291 SB_LINEDOWN, 0); /* line down beyond bot */
2292 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2294 ok(r == 0x00010000,
2295 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2296 ok(y_before == y_after,
2297 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2298 y_before, y_after);
2300 DestroyWindow(hwndRichEdit);
2303 unsigned int recursionLevel = 0;
2304 unsigned int WM_SIZE_recursionLevel = 0;
2305 BOOL bailedOutOfRecursion = FALSE;
2306 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2308 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2310 LRESULT r;
2312 if (bailedOutOfRecursion) return 0;
2313 if (recursionLevel >= 32) {
2314 bailedOutOfRecursion = TRUE;
2315 return 0;
2318 recursionLevel++;
2319 switch (message) {
2320 case WM_SIZE:
2321 WM_SIZE_recursionLevel++;
2322 r = richeditProc(hwnd, message, wParam, lParam);
2323 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2324 ShowScrollBar(hwnd, SB_VERT, TRUE);
2325 WM_SIZE_recursionLevel--;
2326 break;
2327 default:
2328 r = richeditProc(hwnd, message, wParam, lParam);
2329 break;
2331 recursionLevel--;
2332 return r;
2335 static void test_scrollbar_visibility(void)
2337 HWND hwndRichEdit;
2338 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2339 SCROLLINFO si;
2340 WNDCLASSA cls;
2341 BOOL r;
2343 /* These tests show that richedit should temporarily refrain from automatically
2344 hiding or showing its scrollbars (vertical at least) when an explicit request
2345 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2346 Some applications depend on forced showing (when otherwise richedit would
2347 hide the vertical scrollbar) and are thrown on an endless recursive loop
2348 if richedit auto-hides the scrollbar again. Apparently they never heard of
2349 the ES_DISABLENOSCROLL style... */
2351 hwndRichEdit = new_richedit(NULL);
2353 /* Test default scrollbar visibility behavior */
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, 0);
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 visible, should be invisible.\n");
2371 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2372 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2373 si.nPage, si.nMin, si.nMax);
2375 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2376 memset(&si, 0, sizeof(si));
2377 si.cbSize = sizeof(si);
2378 si.fMask = SIF_PAGE | SIF_RANGE;
2379 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2380 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2381 "Vertical scrollbar is invisible, should be visible.\n");
2382 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2383 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2384 si.nPage, si.nMin, si.nMax);
2386 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2387 even though it hides the scrollbar */
2388 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2389 memset(&si, 0, sizeof(si));
2390 si.cbSize = sizeof(si);
2391 si.fMask = SIF_PAGE | SIF_RANGE;
2392 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2393 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2394 "Vertical scrollbar is visible, should be invisible.\n");
2395 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2396 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2397 si.nPage, si.nMin, si.nMax);
2399 /* Setting non-scrolling text again does *not* reset scrollbar range */
2400 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
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, 0);
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)"a");
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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2434 memset(&si, 0, sizeof(si));
2435 si.cbSize = sizeof(si);
2436 si.fMask = SIF_PAGE | SIF_RANGE;
2437 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2438 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2439 "Vertical scrollbar is visible, should be invisible.\n");
2440 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2441 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2442 si.nPage, si.nMin, si.nMax);
2444 DestroyWindow(hwndRichEdit);
2446 /* Test again, with ES_DISABLENOSCROLL style */
2447 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2449 /* Test default scrollbar visibility behavior */
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, 0);
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) expected 0 (0..1)\n",
2469 si.nPage, si.nMin, si.nMax);
2471 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2472 memset(&si, 0, sizeof(si));
2473 si.cbSize = sizeof(si);
2474 si.fMask = SIF_PAGE | SIF_RANGE;
2475 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2476 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2477 "Vertical scrollbar is invisible, should be visible.\n");
2478 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2479 "reported page/range is %d (%d..%d)\n",
2480 si.nPage, si.nMin, si.nMax);
2482 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2483 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2484 memset(&si, 0, sizeof(si));
2485 si.cbSize = sizeof(si);
2486 si.fMask = SIF_PAGE | SIF_RANGE;
2487 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2488 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2489 "Vertical scrollbar is invisible, should be visible.\n");
2490 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2491 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2492 si.nPage, si.nMin, si.nMax);
2494 /* Setting non-scrolling text again does *not* reset scrollbar range */
2495 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
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, 0);
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)"a");
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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2529 memset(&si, 0, sizeof(si));
2530 si.cbSize = sizeof(si);
2531 si.fMask = SIF_PAGE | SIF_RANGE;
2532 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2533 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2534 "Vertical scrollbar is invisible, should be visible.\n");
2535 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2536 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2537 si.nPage, si.nMin, si.nMax);
2539 DestroyWindow(hwndRichEdit);
2541 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2542 hwndRichEdit = new_richedit(NULL);
2544 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2545 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2546 memset(&si, 0, sizeof(si));
2547 si.cbSize = sizeof(si);
2548 si.fMask = SIF_PAGE | SIF_RANGE;
2549 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2550 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2551 "Vertical scrollbar is invisible, should be visible.\n");
2552 todo_wine {
2553 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2554 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2555 si.nPage, si.nMin, si.nMax);
2558 /* Ditto, see above */
2559 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2560 memset(&si, 0, sizeof(si));
2561 si.cbSize = sizeof(si);
2562 si.fMask = SIF_PAGE | SIF_RANGE;
2563 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2564 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2565 "Vertical scrollbar is invisible, should be visible.\n");
2566 todo_wine {
2567 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2568 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2569 si.nPage, si.nMin, si.nMax);
2572 /* Ditto, see above */
2573 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2574 memset(&si, 0, sizeof(si));
2575 si.cbSize = sizeof(si);
2576 si.fMask = SIF_PAGE | SIF_RANGE;
2577 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2578 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2579 "Vertical scrollbar is invisible, should be visible.\n");
2580 todo_wine {
2581 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2582 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2583 si.nPage, si.nMin, si.nMax);
2586 /* Ditto, see above */
2587 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2588 memset(&si, 0, sizeof(si));
2589 si.cbSize = sizeof(si);
2590 si.fMask = SIF_PAGE | SIF_RANGE;
2591 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2592 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2593 "Vertical scrollbar is invisible, should be visible.\n");
2594 todo_wine {
2595 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2596 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2597 si.nPage, si.nMin, si.nMax);
2600 /* Ditto, see above */
2601 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2602 memset(&si, 0, sizeof(si));
2603 si.cbSize = sizeof(si);
2604 si.fMask = SIF_PAGE | SIF_RANGE;
2605 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2606 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2607 "Vertical scrollbar is invisible, should be visible.\n");
2608 todo_wine {
2609 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2610 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2611 si.nPage, si.nMin, si.nMax);
2614 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2615 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2616 memset(&si, 0, sizeof(si));
2617 si.cbSize = sizeof(si);
2618 si.fMask = SIF_PAGE | SIF_RANGE;
2619 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2620 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2621 "Vertical scrollbar is visible, should be invisible.\n");
2622 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2623 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2624 si.nPage, si.nMin, si.nMax);
2626 DestroyWindow(hwndRichEdit);
2628 hwndRichEdit = new_richedit(NULL);
2630 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
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, 0);
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, (LPARAM)"a");
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, 0);
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 visible, should be invisible.\n");
2670 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2671 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2672 si.nPage, si.nMin, si.nMax);
2674 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2675 memset(&si, 0, sizeof(si));
2676 si.cbSize = sizeof(si);
2677 si.fMask = SIF_PAGE | SIF_RANGE;
2678 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2679 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2680 "Vertical scrollbar is invisible, should be visible.\n");
2681 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2682 "reported page/range is %d (%d..%d)\n",
2683 si.nPage, si.nMin, si.nMax);
2685 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2686 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2698 memset(&si, 0, sizeof(si));
2699 si.cbSize = sizeof(si);
2700 si.fMask = SIF_PAGE | SIF_RANGE;
2701 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2702 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2703 "Vertical scrollbar is visible, should be invisible.\n");
2704 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2705 "reported page/range is %d (%d..%d)\n",
2706 si.nPage, si.nMin, si.nMax);
2708 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2709 EM_SCROLL will make visible any forcefully invisible scrollbar */
2710 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
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 invisible, should be visible.\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 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2722 memset(&si, 0, sizeof(si));
2723 si.cbSize = sizeof(si);
2724 si.fMask = SIF_PAGE | SIF_RANGE;
2725 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2726 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2727 "Vertical scrollbar is visible, should be invisible.\n");
2728 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2729 "reported page/range is %d (%d..%d)\n",
2730 si.nPage, si.nMin, si.nMax);
2732 /* Again, EM_SCROLL, with SB_LINEUP */
2733 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 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 invisible, should be visible.\n");
2740 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2741 "reported page/range is %d (%d..%d)\n",
2742 si.nPage, si.nMin, si.nMax);
2744 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
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 visible, should be invisible.\n");
2751 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2752 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2753 si.nPage, si.nMin, si.nMax);
2755 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2756 memset(&si, 0, sizeof(si));
2757 si.cbSize = sizeof(si);
2758 si.fMask = SIF_PAGE | SIF_RANGE;
2759 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2760 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2761 "Vertical scrollbar is invisible, should be visible.\n");
2762 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2763 "reported page/range is %d (%d..%d)\n",
2764 si.nPage, si.nMin, si.nMax);
2766 DestroyWindow(hwndRichEdit);
2769 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2770 hwndRichEdit = new_richedit(NULL);
2772 #define ENABLE_WS_VSCROLL(hwnd) \
2773 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2774 #define DISABLE_WS_VSCROLL(hwnd) \
2775 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2777 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2778 ENABLE_WS_VSCROLL(hwndRichEdit);
2779 memset(&si, 0, sizeof(si));
2780 si.cbSize = sizeof(si);
2781 si.fMask = SIF_PAGE | SIF_RANGE;
2782 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2783 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2784 "Vertical scrollbar is invisible, should be visible.\n");
2785 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2786 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2787 si.nPage, si.nMin, si.nMax);
2789 /* Ditto, see above */
2790 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2791 memset(&si, 0, sizeof(si));
2792 si.cbSize = sizeof(si);
2793 si.fMask = SIF_PAGE | SIF_RANGE;
2794 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2795 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2796 "Vertical scrollbar is invisible, should be visible.\n");
2797 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2798 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2799 si.nPage, si.nMin, si.nMax);
2801 /* Ditto, see above */
2802 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2803 memset(&si, 0, sizeof(si));
2804 si.cbSize = sizeof(si);
2805 si.fMask = SIF_PAGE | SIF_RANGE;
2806 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2807 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2808 "Vertical scrollbar is invisible, should be visible.\n");
2809 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2810 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2811 si.nPage, si.nMin, si.nMax);
2813 /* Ditto, see above */
2814 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2815 memset(&si, 0, sizeof(si));
2816 si.cbSize = sizeof(si);
2817 si.fMask = SIF_PAGE | SIF_RANGE;
2818 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2819 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2820 "Vertical scrollbar is invisible, should be visible.\n");
2821 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2822 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2823 si.nPage, si.nMin, si.nMax);
2825 /* Ditto, see above */
2826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2827 memset(&si, 0, sizeof(si));
2828 si.cbSize = sizeof(si);
2829 si.fMask = SIF_PAGE | SIF_RANGE;
2830 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2832 "Vertical scrollbar is invisible, should be visible.\n");
2833 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2834 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2835 si.nPage, si.nMin, si.nMax);
2837 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2838 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2839 memset(&si, 0, sizeof(si));
2840 si.cbSize = sizeof(si);
2841 si.fMask = SIF_PAGE | SIF_RANGE;
2842 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2843 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2844 "Vertical scrollbar is visible, should be invisible.\n");
2845 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2846 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2847 si.nPage, si.nMin, si.nMax);
2849 DestroyWindow(hwndRichEdit);
2851 hwndRichEdit = new_richedit(NULL);
2853 DISABLE_WS_VSCROLL(hwndRichEdit);
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, 0);
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, (LPARAM)"a");
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, 0);
2887 memset(&si, 0, sizeof(si));
2888 si.cbSize = sizeof(si);
2889 si.fMask = SIF_PAGE | SIF_RANGE;
2890 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2891 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2892 "Vertical scrollbar is visible, should be invisible.\n");
2893 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2894 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2895 si.nPage, si.nMin, si.nMax);
2897 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2898 memset(&si, 0, sizeof(si));
2899 si.cbSize = sizeof(si);
2900 si.fMask = SIF_PAGE | SIF_RANGE;
2901 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2902 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2903 "Vertical scrollbar is invisible, should be visible.\n");
2904 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2905 "reported page/range is %d (%d..%d)\n",
2906 si.nPage, si.nMin, si.nMax);
2908 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2909 DISABLE_WS_VSCROLL(hwndRichEdit);
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)\n",
2918 si.nPage, si.nMin, si.nMax);
2920 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2921 memset(&si, 0, sizeof(si));
2922 si.cbSize = sizeof(si);
2923 si.fMask = SIF_PAGE | SIF_RANGE;
2924 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2925 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2926 "Vertical scrollbar is visible, should be invisible.\n");
2927 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2928 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2929 si.nPage, si.nMin, si.nMax);
2931 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2932 memset(&si, 0, sizeof(si));
2933 si.cbSize = sizeof(si);
2934 si.fMask = SIF_PAGE | SIF_RANGE;
2935 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2936 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2937 "Vertical scrollbar is invisible, should be visible.\n");
2938 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2939 "reported page/range is %d (%d..%d)\n",
2940 si.nPage, si.nMin, si.nMax);
2942 DISABLE_WS_VSCROLL(hwndRichEdit);
2943 memset(&si, 0, sizeof(si));
2944 si.cbSize = sizeof(si);
2945 si.fMask = SIF_PAGE | SIF_RANGE;
2946 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2947 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2948 "Vertical scrollbar is visible, should be invisible.\n");
2949 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2950 "reported page/range is %d (%d..%d)\n",
2951 si.nPage, si.nMin, si.nMax);
2953 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2954 EM_SCROLL will make visible any forcefully invisible scrollbar */
2955 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
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 invisible, should be visible.\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 DISABLE_WS_VSCROLL(hwndRichEdit);
2967 memset(&si, 0, sizeof(si));
2968 si.cbSize = sizeof(si);
2969 si.fMask = SIF_PAGE | SIF_RANGE;
2970 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2971 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2972 "Vertical scrollbar is visible, should be invisible.\n");
2973 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2974 "reported page/range is %d (%d..%d)\n",
2975 si.nPage, si.nMin, si.nMax);
2977 /* Again, EM_SCROLL, with SB_LINEUP */
2978 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2979 memset(&si, 0, sizeof(si));
2980 si.cbSize = sizeof(si);
2981 si.fMask = SIF_PAGE | SIF_RANGE;
2982 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2983 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2984 "Vertical scrollbar is invisible, should be visible.\n");
2985 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2986 "reported page/range is %d (%d..%d)\n",
2987 si.nPage, si.nMin, si.nMax);
2989 DestroyWindow(hwndRichEdit);
2991 /* This window proc models what is going on with Corman Lisp 3.0.
2992 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
2993 force the scrollbar into visibility. Recursion should NOT happen
2994 as a result of this action.
2996 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
2997 if (r) {
2998 richeditProc = cls.lpfnWndProc;
2999 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3000 cls.lpszClassName = "RicheditStupidOverride";
3001 if(!RegisterClassA(&cls)) assert(0);
3003 recursionLevel = 0;
3004 WM_SIZE_recursionLevel = 0;
3005 bailedOutOfRecursion = FALSE;
3006 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3007 ok(!bailedOutOfRecursion,
3008 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3010 recursionLevel = 0;
3011 WM_SIZE_recursionLevel = 0;
3012 bailedOutOfRecursion = FALSE;
3013 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3014 ok(!bailedOutOfRecursion,
3015 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3017 /* Unblock window in order to process WM_DESTROY */
3018 recursionLevel = 0;
3019 bailedOutOfRecursion = FALSE;
3020 WM_SIZE_recursionLevel = 0;
3021 DestroyWindow(hwndRichEdit);
3025 static void test_EM_SETUNDOLIMIT(void)
3027 /* cases we test for:
3028 * default behaviour - limiting at 100 undo's
3029 * undo disabled - setting a limit of 0
3030 * undo limited - undo limit set to some to some number, like 2
3031 * bad input - sending a negative number should default to 100 undo's */
3033 HWND hwndRichEdit = new_richedit(NULL);
3034 CHARRANGE cr;
3035 int i;
3036 int result;
3038 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3039 cr.cpMin = 0;
3040 cr.cpMax = 1;
3041 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3042 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3043 also, multiple pastes don't combine like WM_CHAR would */
3044 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3046 /* first case - check the default */
3047 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3048 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3049 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3050 for (i=0; i<100; i++) /* Undo 100 of them */
3051 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3052 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3053 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3055 /* second case - cannot undo */
3056 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3057 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3058 SendMessage(hwndRichEdit,
3059 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3060 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3061 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3063 /* third case - set it to an arbitrary number */
3064 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3065 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3066 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3067 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3068 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3069 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3070 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3071 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3072 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3073 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3074 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3075 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3076 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3077 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3079 /* fourth case - setting negative numbers should default to 100 undos */
3080 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3081 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3082 ok (result == 100,
3083 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3085 DestroyWindow(hwndRichEdit);
3088 static void test_ES_PASSWORD(void)
3090 /* This isn't hugely testable, so we're just going to run it through its paces */
3092 HWND hwndRichEdit = new_richedit(NULL);
3093 WCHAR result;
3095 /* First, check the default of a regular control */
3096 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3097 ok (result == 0,
3098 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3100 /* Now, set it to something normal */
3101 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3102 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3103 ok (result == 120,
3104 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3106 /* Now, set it to something odd */
3107 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3108 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3109 ok (result == 1234,
3110 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3111 DestroyWindow(hwndRichEdit);
3114 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3115 LPBYTE pbBuff,
3116 LONG cb,
3117 LONG *pcb)
3119 char** str = (char**)dwCookie;
3120 *pcb = cb;
3121 if (*pcb > 0) {
3122 memcpy(*str, pbBuff, *pcb);
3123 *str += *pcb;
3125 return 0;
3128 static void test_WM_SETTEXT()
3130 HWND hwndRichEdit = new_richedit(NULL);
3131 const char * TestItem1 = "TestSomeText";
3132 const char * TestItem2 = "TestSomeText\r";
3133 const char * TestItem2_after = "TestSomeText\r\n";
3134 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3135 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3136 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3137 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3138 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3139 const char * TestItem5_after = "TestSomeText TestSomeText";
3140 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3141 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3142 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3143 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3145 char buf[1024] = {0};
3146 LRESULT result;
3147 EDITSTREAM es;
3148 char * p;
3150 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3151 any solitary \r to be converted to \r\n on return. Properly paired
3152 \r\n are not affected. It also shows that the special sequence \r\r\n
3153 gets converted to a single space.
3156 #define TEST_SETTEXT(a, b) \
3157 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3158 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3159 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3160 ok (result == lstrlen(buf), \
3161 "WM_GETTEXT returned %ld instead of expected %u\n", \
3162 result, lstrlen(buf)); \
3163 result = strcmp(b, buf); \
3164 ok(result == 0, \
3165 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3167 TEST_SETTEXT(TestItem1, TestItem1)
3168 TEST_SETTEXT(TestItem2, TestItem2_after)
3169 TEST_SETTEXT(TestItem3, TestItem3_after)
3170 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3171 TEST_SETTEXT(TestItem4, TestItem4_after)
3172 TEST_SETTEXT(TestItem5, TestItem5_after)
3173 TEST_SETTEXT(TestItem6, TestItem6_after)
3174 TEST_SETTEXT(TestItem7, TestItem7_after)
3176 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3177 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3178 p = buf;
3179 es.dwCookie = (DWORD_PTR)&p;
3180 es.dwError = 0;
3181 es.pfnCallback = test_WM_SETTEXT_esCallback;
3182 memset(buf, 0, sizeof(buf));
3183 SendMessage(hwndRichEdit, EM_STREAMOUT,
3184 (WPARAM)(SF_RTF), (LPARAM)&es);
3185 trace("EM_STREAMOUT produced: \n%s\n", buf);
3186 TEST_SETTEXT(buf, TestItem1)
3188 #undef TEST_SETTEXT
3189 DestroyWindow(hwndRichEdit);
3192 static void test_EM_STREAMOUT(void)
3194 HWND hwndRichEdit = new_richedit(NULL);
3195 int r;
3196 EDITSTREAM es;
3197 char buf[1024] = {0};
3198 char * p;
3200 const char * TestItem1 = "TestSomeText";
3201 const char * TestItem2 = "TestSomeText\r";
3202 const char * TestItem3 = "TestSomeText\r\n";
3204 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3205 p = buf;
3206 es.dwCookie = (DWORD_PTR)&p;
3207 es.dwError = 0;
3208 es.pfnCallback = test_WM_SETTEXT_esCallback;
3209 memset(buf, 0, sizeof(buf));
3210 SendMessage(hwndRichEdit, EM_STREAMOUT,
3211 (WPARAM)(SF_TEXT), (LPARAM)&es);
3212 r = strlen(buf);
3213 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3214 ok(strcmp(buf, TestItem1) == 0,
3215 "streamed text different, got %s\n", buf);
3217 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3218 p = buf;
3219 es.dwCookie = (DWORD_PTR)&p;
3220 es.dwError = 0;
3221 es.pfnCallback = test_WM_SETTEXT_esCallback;
3222 memset(buf, 0, sizeof(buf));
3223 SendMessage(hwndRichEdit, EM_STREAMOUT,
3224 (WPARAM)(SF_TEXT), (LPARAM)&es);
3225 r = strlen(buf);
3226 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3227 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3228 ok(strcmp(buf, TestItem3) == 0,
3229 "streamed text different from, got %s\n", buf);
3230 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3231 p = buf;
3232 es.dwCookie = (DWORD_PTR)&p;
3233 es.dwError = 0;
3234 es.pfnCallback = test_WM_SETTEXT_esCallback;
3235 memset(buf, 0, sizeof(buf));
3236 SendMessage(hwndRichEdit, EM_STREAMOUT,
3237 (WPARAM)(SF_TEXT), (LPARAM)&es);
3238 r = strlen(buf);
3239 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3240 ok(strcmp(buf, TestItem3) == 0,
3241 "streamed text different, got %s\n", buf);
3243 DestroyWindow(hwndRichEdit);
3246 static void test_EM_SETTEXTEX(void)
3248 HWND hwndRichEdit = new_richedit(NULL);
3249 SETTEXTEX setText;
3250 GETTEXTEX getText;
3251 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3252 'S', 'o', 'm', 'e',
3253 'T', 'e', 'x', 't', 0};
3254 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3255 't', 'S', 'o', 'm',
3256 'e', 'T', 'e', 'x',
3257 't', 't', 'S', 'o',
3258 'm', 'e', 'T', 'e',
3259 'x', 't', 0};
3260 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3261 '\r','t','S','o','m','e','T','e','x','t',0};
3262 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3263 'S', 'o', 'm', 'e',
3264 'T', 'e', 'x', 't',
3265 '\r', 0};
3266 const char * TestItem2_after = "TestSomeText\r\n";
3267 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3268 'S', 'o', 'm', 'e',
3269 'T', 'e', 'x', 't',
3270 '\r','\n','\r','\n', 0};
3271 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3272 'S', 'o', 'm', 'e',
3273 'T', 'e', 'x', 't',
3274 '\n','\n', 0};
3275 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3276 'S', 'o', 'm', 'e',
3277 'T', 'e', 'x', 't',
3278 '\r','\r', 0};
3279 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3280 'S', 'o', 'm', 'e',
3281 'T', 'e', 'x', 't',
3282 '\r','\r','\n','\r',
3283 '\n', 0};
3284 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3285 'S', 'o', 'm', 'e',
3286 'T', 'e', 'x', 't',
3287 ' ','\r', 0};
3288 #define MAX_BUF_LEN 1024
3289 WCHAR buf[MAX_BUF_LEN];
3290 char bufACP[MAX_BUF_LEN];
3291 char * p;
3292 int result;
3293 CHARRANGE cr;
3294 EDITSTREAM es;
3296 setText.codepage = 1200; /* no constant for unicode */
3297 getText.codepage = 1200; /* no constant for unicode */
3298 getText.cb = MAX_BUF_LEN;
3299 getText.flags = GT_DEFAULT;
3300 getText.lpDefaultChar = NULL;
3301 getText.lpUsedDefChar = NULL;
3303 setText.flags = 0;
3304 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3305 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3306 ok(lstrcmpW(buf, TestItem1) == 0,
3307 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3309 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3310 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3312 setText.codepage = 1200; /* no constant for unicode */
3313 getText.codepage = 1200; /* no constant for unicode */
3314 getText.cb = MAX_BUF_LEN;
3315 getText.flags = GT_DEFAULT;
3316 getText.lpDefaultChar = NULL;
3317 getText.lpUsedDefChar = NULL;
3318 setText.flags = 0;
3319 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3320 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3321 ok(lstrcmpW(buf, TestItem2) == 0,
3322 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3324 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3325 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3326 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3327 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3329 /* Baseline test for just-enough buffer space for string */
3330 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3331 getText.codepage = 1200; /* no constant for unicode */
3332 getText.flags = GT_DEFAULT;
3333 getText.lpDefaultChar = NULL;
3334 getText.lpUsedDefChar = NULL;
3335 memset(buf, 0, MAX_BUF_LEN);
3336 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3337 ok(lstrcmpW(buf, TestItem2) == 0,
3338 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3340 /* When there is enough space for one character, but not both, of the CRLF
3341 pair at the end of the string, the CR is not copied at all. That is,
3342 the caller must not see CRLF pairs truncated to CR at the end of the
3343 string.
3345 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3346 getText.codepage = 1200; /* no constant for unicode */
3347 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3348 getText.lpDefaultChar = NULL;
3349 getText.lpUsedDefChar = NULL;
3350 memset(buf, 0, MAX_BUF_LEN);
3351 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3352 ok(lstrcmpW(buf, TestItem1) == 0,
3353 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3356 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3357 setText.codepage = 1200; /* no constant for unicode */
3358 getText.codepage = 1200; /* no constant for unicode */
3359 getText.cb = MAX_BUF_LEN;
3360 getText.flags = GT_DEFAULT;
3361 getText.lpDefaultChar = NULL;
3362 getText.lpUsedDefChar = NULL;
3363 setText.flags = 0;
3364 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3365 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3366 ok(lstrcmpW(buf, TestItem3_after) == 0,
3367 "EM_SETTEXTEX did not convert properly\n");
3369 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3370 setText.codepage = 1200; /* no constant for unicode */
3371 getText.codepage = 1200; /* no constant for unicode */
3372 getText.cb = MAX_BUF_LEN;
3373 getText.flags = GT_DEFAULT;
3374 getText.lpDefaultChar = NULL;
3375 getText.lpUsedDefChar = NULL;
3376 setText.flags = 0;
3377 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3378 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3379 ok(lstrcmpW(buf, TestItem3_after) == 0,
3380 "EM_SETTEXTEX did not convert properly\n");
3382 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3383 setText.codepage = 1200; /* no constant for unicode */
3384 getText.codepage = 1200; /* no constant for unicode */
3385 getText.cb = MAX_BUF_LEN;
3386 getText.flags = GT_DEFAULT;
3387 getText.lpDefaultChar = NULL;
3388 getText.lpUsedDefChar = NULL;
3389 setText.flags = 0;
3390 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3391 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3392 ok(lstrcmpW(buf, TestItem4_after) == 0,
3393 "EM_SETTEXTEX did not convert properly\n");
3395 /* !ST_SELECTION && Unicode && !\rtf */
3396 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3397 (WPARAM)&setText, (LPARAM) NULL);
3398 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3400 ok (result == 1,
3401 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3402 ok(lstrlenW(buf) == 0,
3403 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3405 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3406 setText.flags = 0;
3407 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3408 /* select some text */
3409 cr.cpMax = 1;
3410 cr.cpMin = 3;
3411 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3412 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3413 setText.flags = ST_SELECTION;
3414 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3415 (WPARAM)&setText, (LPARAM) NULL);
3416 ok(result == 0,
3417 "EM_SETTEXTEX with NULL lParam to replace selection"
3418 " with no text should return 0. Got %i\n",
3419 result);
3421 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3422 setText.flags = 0;
3423 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3424 /* select some text */
3425 cr.cpMax = 1;
3426 cr.cpMin = 3;
3427 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3428 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3429 setText.flags = ST_SELECTION;
3430 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3431 (WPARAM)&setText, (LPARAM) TestItem1);
3432 /* get text */
3433 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3434 ok(result == lstrlenW(TestItem1),
3435 "EM_SETTEXTEX with NULL lParam to replace selection"
3436 " with no text should return 0. Got %i\n",
3437 result);
3438 ok(lstrlenW(buf) == 22,
3439 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3440 lstrlenW(buf) );
3442 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3443 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3444 p = (char *)buf;
3445 es.dwCookie = (DWORD_PTR)&p;
3446 es.dwError = 0;
3447 es.pfnCallback = test_WM_SETTEXT_esCallback;
3448 memset(buf, 0, sizeof(buf));
3449 SendMessage(hwndRichEdit, EM_STREAMOUT,
3450 (WPARAM)(SF_RTF), (LPARAM)&es);
3451 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3453 /* !ST_SELECTION && !Unicode && \rtf */
3454 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3455 getText.codepage = 1200; /* no constant for unicode */
3456 getText.cb = MAX_BUF_LEN;
3457 getText.flags = GT_DEFAULT;
3458 getText.lpDefaultChar = NULL;
3459 getText.lpUsedDefChar = NULL;
3461 setText.flags = 0;
3462 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3463 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3464 ok(lstrcmpW(buf, TestItem1) == 0,
3465 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3467 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3468 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3469 p = (char *)buf;
3470 es.dwCookie = (DWORD_PTR)&p;
3471 es.dwError = 0;
3472 es.pfnCallback = test_WM_SETTEXT_esCallback;
3473 memset(buf, 0, sizeof(buf));
3474 SendMessage(hwndRichEdit, EM_STREAMOUT,
3475 (WPARAM)(SF_RTF), (LPARAM)&es);
3476 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3478 /* select some text */
3479 cr.cpMax = 1;
3480 cr.cpMin = 3;
3481 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3483 /* ST_SELECTION && !Unicode && \rtf */
3484 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3485 getText.codepage = 1200; /* no constant for unicode */
3486 getText.cb = MAX_BUF_LEN;
3487 getText.flags = GT_DEFAULT;
3488 getText.lpDefaultChar = NULL;
3489 getText.lpUsedDefChar = NULL;
3491 setText.flags = ST_SELECTION;
3492 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3493 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3494 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3496 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3497 setText.codepage = 1200; /* no constant for unicode */
3498 getText.codepage = CP_ACP;
3499 getText.cb = MAX_BUF_LEN;
3501 setText.flags = 0;
3502 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3503 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3505 /* select some text */
3506 cr.cpMax = 1;
3507 cr.cpMin = 3;
3508 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3510 /* ST_SELECTION && !Unicode && !\rtf */
3511 setText.codepage = CP_ACP;
3512 getText.codepage = 1200; /* no constant for unicode */
3513 getText.cb = MAX_BUF_LEN;
3514 getText.flags = GT_DEFAULT;
3515 getText.lpDefaultChar = NULL;
3516 getText.lpUsedDefChar = NULL;
3518 setText.flags = ST_SELECTION;
3519 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3520 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3521 ok(lstrcmpW(buf, TestItem1alt) == 0,
3522 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3523 " using ST_SELECTION and non-Unicode\n");
3525 /* Test setting text using rich text format */
3526 setText.flags = 0;
3527 setText.codepage = CP_ACP;
3528 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3529 getText.codepage = CP_ACP;
3530 getText.cb = MAX_BUF_LEN;
3531 getText.flags = GT_DEFAULT;
3532 getText.lpDefaultChar = NULL;
3533 getText.lpUsedDefChar = NULL;
3534 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3535 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3537 setText.flags = 0;
3538 setText.codepage = CP_ACP;
3539 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3540 getText.codepage = CP_ACP;
3541 getText.cb = MAX_BUF_LEN;
3542 getText.flags = GT_DEFAULT;
3543 getText.lpDefaultChar = NULL;
3544 getText.lpUsedDefChar = NULL;
3545 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3546 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3548 DestroyWindow(hwndRichEdit);
3551 static void test_EM_LIMITTEXT(void)
3553 int ret;
3555 HWND hwndRichEdit = new_richedit(NULL);
3557 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3558 * about setting the length to -1 for multiline edit controls doesn't happen.
3561 /* Don't check default gettextlimit case. That's done in other tests */
3563 /* Set textlimit to 100 */
3564 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3565 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3566 ok (ret == 100,
3567 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3569 /* Set textlimit to 0 */
3570 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3571 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3572 ok (ret == 65536,
3573 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3575 /* Set textlimit to -1 */
3576 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3577 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3578 ok (ret == -1,
3579 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3581 /* Set textlimit to -2 */
3582 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3583 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3584 ok (ret == -2,
3585 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3587 DestroyWindow (hwndRichEdit);
3591 static void test_EM_EXLIMITTEXT(void)
3593 int i, selBegin, selEnd, len1, len2;
3594 int result;
3595 char text[1024 + 1];
3596 char buffer[1024 + 1];
3597 int textlimit = 0; /* multiple of 100 */
3598 HWND hwndRichEdit = new_richedit(NULL);
3600 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3601 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3603 textlimit = 256000;
3604 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3605 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3606 /* set higher */
3607 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3609 textlimit = 1000;
3610 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3611 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3612 /* set lower */
3613 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3615 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3616 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3617 /* default for WParam = 0 */
3618 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3620 textlimit = sizeof(text)-1;
3621 memset(text, 'W', textlimit);
3622 text[sizeof(text)-1] = 0;
3623 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3624 /* maxed out text */
3625 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3627 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3628 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3629 len1 = selEnd - selBegin;
3631 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3632 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3633 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3634 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3635 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3636 len2 = selEnd - selBegin;
3638 ok(len1 != len2,
3639 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3640 len1,len2,i);
3642 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3643 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3644 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3645 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3646 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3647 len1 = selEnd - selBegin;
3649 ok(len1 != len2,
3650 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3651 len1,len2,i);
3653 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3654 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3655 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3656 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3657 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3658 len2 = selEnd - selBegin;
3660 ok(len1 == len2,
3661 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3662 len1,len2,i);
3664 /* set text up to the limit, select all the text, then add a char */
3665 textlimit = 5;
3666 memset(text, 'W', textlimit);
3667 text[textlimit] = 0;
3668 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3669 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3670 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3671 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3672 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3673 result = strcmp(buffer, "A");
3674 ok(0 == result, "got string = \"%s\"\n", buffer);
3676 /* WM_SETTEXT not limited */
3677 textlimit = 10;
3678 memset(text, 'W', textlimit);
3679 text[textlimit] = 0;
3680 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3681 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3682 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3683 i = strlen(buffer);
3684 ok(10 == i, "expected 10 chars\n");
3685 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3686 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3688 /* try inserting more text at end */
3689 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3690 ok(0 == i, "WM_CHAR wasn't processed\n");
3691 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3692 i = strlen(buffer);
3693 ok(10 == i, "expected 10 chars, got %i\n", i);
3694 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3695 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3697 /* try inserting text at beginning */
3698 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3699 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3700 ok(0 == i, "WM_CHAR wasn't processed\n");
3701 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3702 i = strlen(buffer);
3703 ok(10 == i, "expected 10 chars, got %i\n", i);
3704 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3705 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3707 /* WM_CHAR is limited */
3708 textlimit = 1;
3709 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3710 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3711 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3712 ok(0 == i, "WM_CHAR wasn't processed\n");
3713 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3714 ok(0 == i, "WM_CHAR wasn't processed\n");
3715 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3716 i = strlen(buffer);
3717 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3719 DestroyWindow(hwndRichEdit);
3722 static void test_EM_GETLIMITTEXT(void)
3724 int i;
3725 HWND hwndRichEdit = new_richedit(NULL);
3727 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3728 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3730 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3731 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3732 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3734 DestroyWindow(hwndRichEdit);
3737 static void test_WM_SETFONT(void)
3739 /* There is no invalid input or error conditions for this function.
3740 * NULL wParam and lParam just fall back to their default values
3741 * It should be noted that even if you use a gibberish name for your fonts
3742 * here, it will still work because the name is stored. They will display as
3743 * System, but will report their name to be whatever they were created as */
3745 HWND hwndRichEdit = new_richedit(NULL);
3746 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3747 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3748 FF_DONTCARE, "Marlett");
3749 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3750 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3751 FF_DONTCARE, "MS Sans Serif");
3752 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3753 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3754 FF_DONTCARE, "Courier");
3755 LOGFONTA sentLogFont;
3756 CHARFORMAT2A returnedCF2A;
3758 returnedCF2A.cbSize = sizeof(returnedCF2A);
3760 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3761 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
3762 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3764 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3765 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3766 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3767 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3769 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
3770 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3771 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3772 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3773 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3774 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3776 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
3777 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3778 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3779 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3780 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3781 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3783 /* This last test is special since we send in NULL. We clear the variables
3784 * and just compare to "System" instead of the sent in font name. */
3785 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3786 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3787 returnedCF2A.cbSize = sizeof(returnedCF2A);
3789 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
3790 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3791 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3792 ok (!strcmp("System",returnedCF2A.szFaceName),
3793 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3795 DestroyWindow(hwndRichEdit);
3799 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3800 LPBYTE pbBuff,
3801 LONG cb,
3802 LONG *pcb)
3804 const char** str = (const char**)dwCookie;
3805 int size = strlen(*str);
3806 if(size > 3) /* let's make it piecemeal for fun */
3807 size = 3;
3808 *pcb = cb;
3809 if (*pcb > size) {
3810 *pcb = size;
3812 if (*pcb > 0) {
3813 memcpy(pbBuff, *str, *pcb);
3814 *str += *pcb;
3816 return 0;
3819 static void test_EM_GETMODIFY(void)
3821 HWND hwndRichEdit = new_richedit(NULL);
3822 LRESULT result;
3823 SETTEXTEX setText;
3824 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3825 'S', 'o', 'm', 'e',
3826 'T', 'e', 'x', 't', 0};
3827 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3828 'S', 'o', 'm', 'e',
3829 'O', 't', 'h', 'e', 'r',
3830 'T', 'e', 'x', 't', 0};
3831 const char* streamText = "hello world";
3832 CHARFORMAT2 cf2;
3833 PARAFORMAT2 pf2;
3834 EDITSTREAM es;
3836 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3837 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3838 FF_DONTCARE, "Courier");
3840 setText.codepage = 1200; /* no constant for unicode */
3841 setText.flags = ST_KEEPUNDO;
3844 /* modify flag shouldn't be set when richedit is first created */
3845 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3846 ok (result == 0,
3847 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
3849 /* setting modify flag should actually set it */
3850 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
3851 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3852 ok (result != 0,
3853 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
3855 /* clearing modify flag should actually clear it */
3856 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3857 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3858 ok (result == 0,
3859 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
3861 /* setting font doesn't change modify flag */
3862 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3863 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
3864 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3865 ok (result == 0,
3866 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
3868 /* setting text should set modify flag */
3869 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3870 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3871 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3872 ok (result != 0,
3873 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
3875 /* undo previous text doesn't reset modify flag */
3876 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3877 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3878 ok (result != 0,
3879 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
3881 /* set text with no flag to keep undo stack should not set modify flag */
3882 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3883 setText.flags = 0;
3884 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3885 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3886 ok (result == 0,
3887 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
3889 /* WM_SETTEXT doesn't modify */
3890 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3891 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3892 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3893 ok (result == 0,
3894 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3896 /* clear the text */
3897 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3898 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3899 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3900 ok (result == 0,
3901 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3903 /* replace text */
3904 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3905 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3906 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3907 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3908 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3909 ok (result != 0,
3910 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3912 /* copy/paste text 1 */
3913 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3914 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3915 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3916 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3917 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3918 ok (result != 0,
3919 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3921 /* copy/paste text 2 */
3922 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3923 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3924 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3925 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3926 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3927 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3928 ok (result != 0,
3929 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3931 /* press char */
3932 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3933 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3934 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3935 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3936 ok (result != 0,
3937 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3939 /* press del */
3940 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3941 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3942 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
3943 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3944 ok (result != 0,
3945 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3947 /* set char format */
3948 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3949 cf2.cbSize = sizeof(CHARFORMAT2);
3950 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3951 (LPARAM) &cf2);
3952 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3953 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3954 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3955 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3956 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3957 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3958 ok (result != 0,
3959 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3961 /* set para format */
3962 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3963 pf2.cbSize = sizeof(PARAFORMAT2);
3964 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3965 (LPARAM) &pf2);
3966 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3967 pf2.wAlignment = PFA_RIGHT;
3968 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3969 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3970 ok (result == 0,
3971 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3973 /* EM_STREAM */
3974 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3975 es.dwCookie = (DWORD_PTR)&streamText;
3976 es.dwError = 0;
3977 es.pfnCallback = test_EM_GETMODIFY_esCallback;
3978 SendMessage(hwndRichEdit, EM_STREAMIN,
3979 (WPARAM)(SF_TEXT), (LPARAM)&es);
3980 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3981 ok (result != 0,
3982 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
3984 DestroyWindow(hwndRichEdit);
3987 struct exsetsel_s {
3988 long min;
3989 long max;
3990 long expected_retval;
3991 int expected_getsel_start;
3992 int expected_getsel_end;
3993 int _exsetsel_todo_wine;
3994 int _getsel_todo_wine;
3997 const struct exsetsel_s exsetsel_tests[] = {
3998 /* sanity tests */
3999 {5, 10, 10, 5, 10, 0, 0},
4000 {15, 17, 17, 15, 17, 0, 0},
4001 /* test cpMax > strlen() */
4002 {0, 100, 18, 0, 18, 0, 1},
4003 /* test cpMin == cpMax */
4004 {5, 5, 5, 5, 5, 0, 0},
4005 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4006 {-1, 0, 5, 5, 5, 0, 0},
4007 {-1, 17, 5, 5, 5, 0, 0},
4008 {-1, 18, 5, 5, 5, 0, 0},
4009 /* test cpMin < 0 && cpMax < 0 */
4010 {-1, -1, 17, 17, 17, 0, 0},
4011 {-4, -5, 17, 17, 17, 0, 0},
4012 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4013 {0, -1, 18, 0, 18, 0, 1},
4014 {17, -5, 18, 17, 18, 0, 1},
4015 {18, -3, 17, 17, 17, 0, 0},
4016 /* test if cpMin > cpMax */
4017 {15, 19, 18, 15, 18, 0, 1},
4018 {19, 15, 18, 15, 18, 0, 1}
4021 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4022 CHARRANGE cr;
4023 long result;
4024 int start, end;
4026 cr.cpMin = setsel->min;
4027 cr.cpMax = setsel->max;
4028 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4030 if (setsel->_exsetsel_todo_wine) {
4031 todo_wine {
4032 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4034 } else {
4035 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4038 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4040 if (setsel->_getsel_todo_wine) {
4041 todo_wine {
4042 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);
4044 } else {
4045 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);
4049 static void test_EM_EXSETSEL(void)
4051 HWND hwndRichEdit = new_richedit(NULL);
4052 int i;
4053 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4055 /* sending some text to the window */
4056 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4057 /* 01234567890123456*/
4058 /* 10 */
4060 for (i = 0; i < num_tests; i++) {
4061 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4064 DestroyWindow(hwndRichEdit);
4067 static void test_EM_REPLACESEL(int redraw)
4069 HWND hwndRichEdit = new_richedit(NULL);
4070 char buffer[1024] = {0};
4071 int r;
4072 GETTEXTEX getText;
4073 CHARRANGE cr;
4075 /* sending some text to the window */
4076 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4077 /* 01234567890123456*/
4078 /* 10 */
4080 /* FIXME add more tests */
4081 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4082 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
4083 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4084 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4085 r = strcmp(buffer, "testing");
4086 ok(0 == r, "expected %d, got %d\n", 0, r);
4088 DestroyWindow(hwndRichEdit);
4090 hwndRichEdit = new_richedit(NULL);
4092 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4093 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4095 /* Test behavior with carriage returns and newlines */
4096 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4097 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4098 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4099 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4100 r = strcmp(buffer, "RichEdit1");
4101 ok(0 == r, "expected %d, got %d\n", 0, r);
4102 getText.cb = 1024;
4103 getText.codepage = CP_ACP;
4104 getText.flags = GT_DEFAULT;
4105 getText.lpDefaultChar = NULL;
4106 getText.lpUsedDefChar = NULL;
4107 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4108 ok(strcmp(buffer, "RichEdit1") == 0,
4109 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4111 /* Test number of lines reported after EM_REPLACESEL */
4112 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4113 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4115 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4116 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4117 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4118 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4119 r = strcmp(buffer, "RichEdit1\r\n");
4120 ok(0 == r, "expected %d, got %d\n", 0, r);
4121 getText.cb = 1024;
4122 getText.codepage = CP_ACP;
4123 getText.flags = GT_DEFAULT;
4124 getText.lpDefaultChar = NULL;
4125 getText.lpUsedDefChar = NULL;
4126 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4127 ok(strcmp(buffer, "RichEdit1\r") == 0,
4128 "EM_GETTEXTEX returned incorrect string\n");
4130 /* Test number of lines reported after EM_REPLACESEL */
4131 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4132 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4134 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4135 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4136 returns the number of characters *inserted* into the control (after
4137 required conversions), but WinXP's riched20 returns the number of
4138 characters interpreted from the original lParam. Wine's builtin riched20
4139 implements the WinXP behavior.
4141 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4142 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4143 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4144 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4146 /* Test number of lines reported after EM_REPLACESEL */
4147 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4148 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4150 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4151 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4152 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4153 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4155 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4156 r = strcmp(buffer, "RichEdit1\r\n");
4157 ok(0 == r, "expected %d, got %d\n", 0, r);
4158 getText.cb = 1024;
4159 getText.codepage = CP_ACP;
4160 getText.flags = GT_DEFAULT;
4161 getText.lpDefaultChar = NULL;
4162 getText.lpUsedDefChar = NULL;
4163 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4164 ok(strcmp(buffer, "RichEdit1\r") == 0,
4165 "EM_GETTEXTEX returned incorrect string\n");
4167 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4168 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4169 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4170 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4172 /* The following tests show that richedit should handle the special \r\r\n
4173 sequence by turning it into a single space on insertion. However,
4174 EM_REPLACESEL on WinXP returns the number of characters in the original
4175 string.
4178 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4179 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4180 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4181 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4182 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4183 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4184 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4186 /* Test the actual string */
4187 getText.cb = 1024;
4188 getText.codepage = CP_ACP;
4189 getText.flags = GT_DEFAULT;
4190 getText.lpDefaultChar = NULL;
4191 getText.lpUsedDefChar = NULL;
4192 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4193 ok(strcmp(buffer, "\r\r") == 0,
4194 "EM_GETTEXTEX returned incorrect string\n");
4196 /* Test number of lines reported after EM_REPLACESEL */
4197 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4198 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4200 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4201 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4202 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4203 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4204 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4205 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4206 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4207 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4209 /* Test the actual string */
4210 getText.cb = 1024;
4211 getText.codepage = CP_ACP;
4212 getText.flags = GT_DEFAULT;
4213 getText.lpDefaultChar = NULL;
4214 getText.lpUsedDefChar = NULL;
4215 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4216 ok(strcmp(buffer, " ") == 0,
4217 "EM_GETTEXTEX returned incorrect string\n");
4219 /* Test number of lines reported after EM_REPLACESEL */
4220 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4221 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4223 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4224 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4225 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4226 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4227 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4228 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4229 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4230 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4232 /* Test the actual string */
4233 getText.cb = 1024;
4234 getText.codepage = CP_ACP;
4235 getText.flags = GT_DEFAULT;
4236 getText.lpDefaultChar = NULL;
4237 getText.lpUsedDefChar = NULL;
4238 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4239 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4240 "EM_GETTEXTEX returned incorrect string\n");
4242 /* Test number of lines reported after EM_REPLACESEL */
4243 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4244 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4246 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4247 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4248 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4249 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4250 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4251 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4252 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4253 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4255 /* Test the actual string */
4256 getText.cb = 1024;
4257 getText.codepage = CP_ACP;
4258 getText.flags = GT_DEFAULT;
4259 getText.lpDefaultChar = NULL;
4260 getText.lpUsedDefChar = NULL;
4261 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4262 ok(strcmp(buffer, " \r") == 0,
4263 "EM_GETTEXTEX returned incorrect string\n");
4265 /* Test number of lines reported after EM_REPLACESEL */
4266 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4267 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4269 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4270 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4271 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4272 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4273 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4274 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4275 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4276 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4278 /* Test the actual string */
4279 getText.cb = 1024;
4280 getText.codepage = CP_ACP;
4281 getText.flags = GT_DEFAULT;
4282 getText.lpDefaultChar = NULL;
4283 getText.lpUsedDefChar = NULL;
4284 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4285 ok(strcmp(buffer, " \r\r") == 0,
4286 "EM_GETTEXTEX returned incorrect string\n");
4288 /* Test number of lines reported after EM_REPLACESEL */
4289 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4290 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4292 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4293 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4294 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4295 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4296 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4297 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4298 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4299 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4301 /* Test the actual string */
4302 getText.cb = 1024;
4303 getText.codepage = CP_ACP;
4304 getText.flags = GT_DEFAULT;
4305 getText.lpDefaultChar = NULL;
4306 getText.lpUsedDefChar = NULL;
4307 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4308 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4309 "EM_GETTEXTEX returned incorrect string\n");
4311 /* Test number of lines reported after EM_REPLACESEL */
4312 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4313 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4315 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4316 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4317 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4318 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4319 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4320 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4321 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4323 /* Test the actual string */
4324 getText.cb = 1024;
4325 getText.codepage = CP_ACP;
4326 getText.flags = GT_DEFAULT;
4327 getText.lpDefaultChar = NULL;
4328 getText.lpUsedDefChar = NULL;
4329 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4330 ok(strcmp(buffer, "\r\r") == 0,
4331 "EM_GETTEXTEX returned incorrect string\n");
4333 /* Test number of lines reported after EM_REPLACESEL */
4334 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4335 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4337 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4338 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4339 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4340 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4341 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4342 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4343 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4344 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4346 /* Test the actual string */
4347 getText.cb = 1024;
4348 getText.codepage = CP_ACP;
4349 getText.flags = GT_DEFAULT;
4350 getText.lpDefaultChar = NULL;
4351 getText.lpUsedDefChar = NULL;
4352 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4353 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4354 "EM_GETTEXTEX returned incorrect string\n");
4356 /* Test number of lines reported after EM_REPLACESEL */
4357 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4358 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4360 if (!redraw)
4361 /* This is needed to avoid interferring with keybd_event calls
4362 * on other tests that simulate keyboard events. */
4363 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4365 DestroyWindow(hwndRichEdit);
4368 static void test_WM_PASTE(void)
4370 int result;
4371 char buffer[1024] = {0};
4372 const char* text1 = "testing paste\r";
4373 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4374 const char* text1_after = "testing paste\r\n";
4375 const char* text2 = "testing paste\r\rtesting paste";
4376 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4377 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4378 HWND hwndRichEdit = new_richedit(NULL);
4380 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
4381 messages, probably because it inspects the keyboard state itself.
4382 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
4385 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4386 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4387 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4388 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4389 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4391 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4392 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4394 SEND_CTRL_C(hwndRichEdit); /* Copy */
4395 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4396 SEND_CTRL_V(hwndRichEdit); /* Paste */
4397 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4398 /* Pasted text should be visible at this step */
4399 result = strcmp(text1_step1, buffer);
4400 ok(result == 0,
4401 "test paste: strcmp = %i\n", result);
4402 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4403 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4404 /* Text should be the same as before (except for \r -> \r\n conversion) */
4405 result = strcmp(text1_after, buffer);
4406 ok(result == 0,
4407 "test paste: strcmp = %i\n", result);
4409 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4410 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4411 SEND_CTRL_C(hwndRichEdit); /* Copy */
4412 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4413 SEND_CTRL_V(hwndRichEdit); /* Paste */
4414 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4415 /* Pasted text should be visible at this step */
4416 result = strcmp(text3, buffer);
4417 ok(result == 0,
4418 "test paste: strcmp = %i\n", result);
4419 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4420 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4421 /* Text should be the same as before (except for \r -> \r\n conversion) */
4422 result = strcmp(text2_after, buffer);
4423 ok(result == 0,
4424 "test paste: strcmp = %i\n", result);
4425 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4426 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4427 /* Text should revert to post-paste state */
4428 result = strcmp(buffer,text3);
4429 ok(result == 0,
4430 "test paste: strcmp = %i\n", result);
4432 DestroyWindow(hwndRichEdit);
4435 static void test_EM_FORMATRANGE(void)
4437 int r;
4438 FORMATRANGE fr;
4439 HDC hdc;
4440 HWND hwndRichEdit = new_richedit(NULL);
4442 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4444 hdc = GetDC(hwndRichEdit);
4445 ok(hdc != NULL, "Could not get HDC\n");
4447 fr.hdc = fr.hdcTarget = hdc;
4448 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4449 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4450 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4451 fr.chrg.cpMin = 0;
4452 fr.chrg.cpMax = 20;
4454 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4455 todo_wine {
4456 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4459 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4460 todo_wine {
4461 ok(r == 20 || r == 9, "EM_FORMATRANGE expect 20 or 9, got %d\n", r);
4464 fr.chrg.cpMin = 0;
4465 fr.chrg.cpMax = 10;
4467 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4468 todo_wine {
4469 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4472 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4473 todo_wine {
4474 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4477 DestroyWindow(hwndRichEdit);
4480 static int nCallbackCount = 0;
4482 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4483 LONG cb, LONG* pcb)
4485 const char text[] = {'t','e','s','t'};
4487 if (sizeof(text) <= cb)
4489 if ((int)dwCookie != nCallbackCount)
4491 *pcb = 0;
4492 return 0;
4495 memcpy (pbBuff, text, sizeof(text));
4496 *pcb = sizeof(text);
4498 nCallbackCount++;
4500 return 0;
4502 else
4503 return 1; /* indicates callback failed */
4506 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4507 LPBYTE pbBuff,
4508 LONG cb,
4509 LONG *pcb)
4511 const char** str = (const char**)dwCookie;
4512 int size = strlen(*str);
4513 *pcb = cb;
4514 if (*pcb > size) {
4515 *pcb = size;
4517 if (*pcb > 0) {
4518 memcpy(pbBuff, *str, *pcb);
4519 *str += *pcb;
4521 return 0;
4524 struct StringWithLength {
4525 int length;
4526 char *buffer;
4529 /* This callback is used to handled the null characters in a string. */
4530 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4531 LPBYTE pbBuff,
4532 LONG cb,
4533 LONG *pcb)
4535 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4536 int size = str->length;
4537 *pcb = cb;
4538 if (*pcb > size) {
4539 *pcb = size;
4541 if (*pcb > 0) {
4542 memcpy(pbBuff, str->buffer, *pcb);
4543 str->buffer += *pcb;
4544 str->length -= *pcb;
4546 return 0;
4549 static void test_EM_STREAMIN(void)
4551 HWND hwndRichEdit = new_richedit(NULL);
4552 LRESULT result;
4553 EDITSTREAM es;
4554 char buffer[1024] = {0};
4556 const char * streamText0 = "{\\rtf1 TestSomeText}";
4557 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4558 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4560 const char * streamText1 =
4561 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
4562 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
4563 "}\r\n";
4565 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4566 const char * streamText2 =
4567 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
4568 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
4569 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
4570 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
4571 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
4572 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
4573 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4575 const char * streamText3 = "RichEdit1";
4577 struct StringWithLength cookieForStream4;
4578 const char * streamText4 =
4579 "This text just needs to be long enough to cause run to be split onto "\
4580 "two separate lines and make sure the null terminating character is "\
4581 "handled properly.\0";
4582 int length4 = strlen(streamText4) + 1;
4583 cookieForStream4.buffer = (char *)streamText4;
4584 cookieForStream4.length = length4;
4586 /* Minimal test without \par at the end */
4587 es.dwCookie = (DWORD_PTR)&streamText0;
4588 es.dwError = 0;
4589 es.pfnCallback = test_EM_STREAMIN_esCallback;
4590 SendMessage(hwndRichEdit, EM_STREAMIN,
4591 (WPARAM)(SF_RTF), (LPARAM)&es);
4593 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4594 ok (result == 12,
4595 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4596 result = strcmp (buffer,"TestSomeText");
4597 ok (result == 0,
4598 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4599 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4601 /* Native richedit 2.0 ignores last \par */
4602 es.dwCookie = (DWORD_PTR)&streamText0a;
4603 es.dwError = 0;
4604 es.pfnCallback = test_EM_STREAMIN_esCallback;
4605 SendMessage(hwndRichEdit, EM_STREAMIN,
4606 (WPARAM)(SF_RTF), (LPARAM)&es);
4608 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4609 ok (result == 12,
4610 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4611 result = strcmp (buffer,"TestSomeText");
4612 ok (result == 0,
4613 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4614 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4616 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4617 es.dwCookie = (DWORD_PTR)&streamText0b;
4618 es.dwError = 0;
4619 es.pfnCallback = test_EM_STREAMIN_esCallback;
4620 SendMessage(hwndRichEdit, EM_STREAMIN,
4621 (WPARAM)(SF_RTF), (LPARAM)&es);
4623 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4624 ok (result == 14,
4625 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4626 result = strcmp (buffer,"TestSomeText\r\n");
4627 ok (result == 0,
4628 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4629 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4631 es.dwCookie = (DWORD_PTR)&streamText1;
4632 es.dwError = 0;
4633 es.pfnCallback = test_EM_STREAMIN_esCallback;
4634 SendMessage(hwndRichEdit, EM_STREAMIN,
4635 (WPARAM)(SF_RTF), (LPARAM)&es);
4637 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4638 ok (result == 12,
4639 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4640 result = strcmp (buffer,"TestSomeText");
4641 ok (result == 0,
4642 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4643 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4645 es.dwCookie = (DWORD_PTR)&streamText2;
4646 es.dwError = 0;
4647 SendMessage(hwndRichEdit, EM_STREAMIN,
4648 (WPARAM)(SF_RTF), (LPARAM)&es);
4650 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4651 ok (result == 0,
4652 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4653 ok (strlen(buffer) == 0,
4654 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4655 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4657 es.dwCookie = (DWORD_PTR)&streamText3;
4658 es.dwError = 0;
4659 SendMessage(hwndRichEdit, EM_STREAMIN,
4660 (WPARAM)(SF_RTF), (LPARAM)&es);
4662 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4663 ok (result == 0,
4664 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4665 ok (strlen(buffer) == 0,
4666 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4667 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4669 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4670 es.dwError = 0;
4671 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4672 SendMessage(hwndRichEdit, EM_STREAMIN,
4673 (WPARAM)(SF_TEXT), (LPARAM)&es);
4675 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4676 ok (result == length4,
4677 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4678 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4680 DestroyWindow(hwndRichEdit);
4683 static void test_EM_StreamIn_Undo(void)
4685 /* The purpose of this test is to determine when a EM_StreamIn should be
4686 * undoable. This is important because WM_PASTE currently uses StreamIn and
4687 * pasting should always be undoable but streaming isn't always.
4689 * cases to test:
4690 * StreamIn plain text without SFF_SELECTION.
4691 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4692 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4693 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4694 * Feel free to add tests for other text modes or StreamIn things.
4698 HWND hwndRichEdit = new_richedit(NULL);
4699 LRESULT result;
4700 EDITSTREAM es;
4701 char buffer[1024] = {0};
4702 const char randomtext[] = "Some text";
4704 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4706 /* StreamIn, no SFF_SELECTION */
4707 es.dwCookie = nCallbackCount;
4708 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4709 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4710 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4711 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4712 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4713 result = strcmp (buffer,"test");
4714 ok (result == 0,
4715 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4717 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4718 ok (result == FALSE,
4719 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4721 /* StreamIn, SFF_SELECTION, but nothing selected */
4722 es.dwCookie = nCallbackCount;
4723 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4724 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4725 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4726 SendMessage(hwndRichEdit, EM_STREAMIN,
4727 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4728 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4729 result = strcmp (buffer,"testSome text");
4730 ok (result == 0,
4731 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4733 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4734 ok (result == TRUE,
4735 "EM_STREAMIN with SFF_SELECTION but no selection set "
4736 "should create an undo\n");
4738 /* StreamIn, SFF_SELECTION, with a selection */
4739 es.dwCookie = nCallbackCount;
4740 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4741 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4742 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
4743 SendMessage(hwndRichEdit, EM_STREAMIN,
4744 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4745 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4746 result = strcmp (buffer,"Sometesttext");
4747 ok (result == 0,
4748 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4750 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4751 ok (result == TRUE,
4752 "EM_STREAMIN with SFF_SELECTION and selection set "
4753 "should create an undo\n");
4755 DestroyWindow(hwndRichEdit);
4758 static BOOL is_em_settextex_supported(HWND hwnd)
4760 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
4761 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
4764 static void test_unicode_conversions(void)
4766 static const WCHAR tW[] = {'t',0};
4767 static const WCHAR teW[] = {'t','e',0};
4768 static const WCHAR textW[] = {'t','e','s','t',0};
4769 static const char textA[] = "test";
4770 char bufA[64];
4771 WCHAR bufW[64];
4772 HWND hwnd;
4773 int is_win9x, em_settextex_supported, ret;
4775 is_win9x = GetVersion() & 0x80000000;
4777 #define set_textA(hwnd, wm_set_text, txt) \
4778 do { \
4779 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
4780 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4781 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4782 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4783 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
4784 } while(0)
4785 #define expect_textA(hwnd, wm_get_text, txt) \
4786 do { \
4787 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4788 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4789 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4790 memset(bufA, 0xAA, sizeof(bufA)); \
4791 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4792 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4793 ret = lstrcmpA(bufA, txt); \
4794 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
4795 } while(0)
4797 #define set_textW(hwnd, wm_set_text, txt) \
4798 do { \
4799 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
4800 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4801 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4802 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4803 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
4804 } while(0)
4805 #define expect_textW(hwnd, wm_get_text, txt) \
4806 do { \
4807 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
4808 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4809 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4810 memset(bufW, 0xAA, sizeof(bufW)); \
4811 if (is_win9x) \
4813 assert(wm_get_text == EM_GETTEXTEX); \
4814 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4815 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4817 else \
4819 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4820 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
4822 ret = lstrcmpW(bufW, txt); \
4823 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
4824 } while(0)
4825 #define expect_empty(hwnd, wm_get_text) \
4826 do { \
4827 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4828 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4829 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4830 memset(bufA, 0xAA, sizeof(bufA)); \
4831 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4832 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
4833 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
4834 } while(0)
4836 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4837 0, 0, 200, 60, 0, 0, 0, 0);
4838 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4840 ret = IsWindowUnicode(hwnd);
4841 if (is_win9x)
4842 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
4843 else
4844 ok(ret, "RichEdit20W should be unicode under NT\n");
4846 /* EM_SETTEXTEX is supported starting from version 3.0 */
4847 em_settextex_supported = is_em_settextex_supported(hwnd);
4848 trace("EM_SETTEXTEX is %ssupported on this platform\n",
4849 em_settextex_supported ? "" : "NOT ");
4851 expect_empty(hwnd, WM_GETTEXT);
4852 expect_empty(hwnd, EM_GETTEXTEX);
4854 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
4855 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4856 expect_textA(hwnd, WM_GETTEXT, "t");
4857 expect_textA(hwnd, EM_GETTEXTEX, "t");
4858 expect_textW(hwnd, EM_GETTEXTEX, tW);
4860 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
4861 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4862 expect_textA(hwnd, WM_GETTEXT, "te");
4863 expect_textA(hwnd, EM_GETTEXTEX, "te");
4864 expect_textW(hwnd, EM_GETTEXTEX, teW);
4866 set_textA(hwnd, WM_SETTEXT, NULL);
4867 expect_empty(hwnd, WM_GETTEXT);
4868 expect_empty(hwnd, EM_GETTEXTEX);
4870 if (is_win9x)
4871 set_textA(hwnd, WM_SETTEXT, textW);
4872 else
4873 set_textA(hwnd, WM_SETTEXT, textA);
4874 expect_textA(hwnd, WM_GETTEXT, textA);
4875 expect_textA(hwnd, EM_GETTEXTEX, textA);
4876 expect_textW(hwnd, EM_GETTEXTEX, textW);
4878 if (em_settextex_supported)
4880 set_textA(hwnd, EM_SETTEXTEX, textA);
4881 expect_textA(hwnd, WM_GETTEXT, textA);
4882 expect_textA(hwnd, EM_GETTEXTEX, textA);
4883 expect_textW(hwnd, EM_GETTEXTEX, textW);
4886 if (!is_win9x)
4888 set_textW(hwnd, WM_SETTEXT, textW);
4889 expect_textW(hwnd, WM_GETTEXT, textW);
4890 expect_textA(hwnd, WM_GETTEXT, textA);
4891 expect_textW(hwnd, EM_GETTEXTEX, textW);
4892 expect_textA(hwnd, EM_GETTEXTEX, textA);
4894 if (em_settextex_supported)
4896 set_textW(hwnd, EM_SETTEXTEX, textW);
4897 expect_textW(hwnd, WM_GETTEXT, textW);
4898 expect_textA(hwnd, WM_GETTEXT, textA);
4899 expect_textW(hwnd, EM_GETTEXTEX, textW);
4900 expect_textA(hwnd, EM_GETTEXTEX, textA);
4903 DestroyWindow(hwnd);
4905 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
4906 0, 0, 200, 60, 0, 0, 0, 0);
4907 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4909 ret = IsWindowUnicode(hwnd);
4910 ok(!ret, "RichEdit20A should NOT be unicode\n");
4912 set_textA(hwnd, WM_SETTEXT, textA);
4913 expect_textA(hwnd, WM_GETTEXT, textA);
4914 expect_textA(hwnd, EM_GETTEXTEX, textA);
4915 expect_textW(hwnd, EM_GETTEXTEX, textW);
4917 if (em_settextex_supported)
4919 set_textA(hwnd, EM_SETTEXTEX, textA);
4920 expect_textA(hwnd, WM_GETTEXT, textA);
4921 expect_textA(hwnd, EM_GETTEXTEX, textA);
4922 expect_textW(hwnd, EM_GETTEXTEX, textW);
4925 if (!is_win9x)
4927 set_textW(hwnd, WM_SETTEXT, textW);
4928 expect_textW(hwnd, WM_GETTEXT, textW);
4929 expect_textA(hwnd, WM_GETTEXT, textA);
4930 expect_textW(hwnd, EM_GETTEXTEX, textW);
4931 expect_textA(hwnd, EM_GETTEXTEX, textA);
4933 if (em_settextex_supported)
4935 set_textW(hwnd, EM_SETTEXTEX, textW);
4936 expect_textW(hwnd, WM_GETTEXT, textW);
4937 expect_textA(hwnd, WM_GETTEXT, textA);
4938 expect_textW(hwnd, EM_GETTEXTEX, textW);
4939 expect_textA(hwnd, EM_GETTEXTEX, textA);
4942 DestroyWindow(hwnd);
4945 static void test_WM_CHAR(void)
4947 HWND hwnd;
4948 int ret;
4949 const char * char_list = "abc\rabc\r";
4950 const char * expected_content_single = "abcabc";
4951 const char * expected_content_multi = "abc\r\nabc\r\n";
4952 char buffer[64] = {0};
4953 const char * p;
4955 /* single-line control must IGNORE carriage returns */
4956 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4957 0, 0, 200, 60, 0, 0, 0, 0);
4958 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4960 p = char_list;
4961 while (*p != '\0') {
4962 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4963 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4964 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4965 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4966 p++;
4969 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4970 ret = strcmp(buffer, expected_content_single);
4971 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4973 DestroyWindow(hwnd);
4975 /* multi-line control inserts CR normally */
4976 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4977 0, 0, 200, 60, 0, 0, 0, 0);
4978 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4980 p = char_list;
4981 while (*p != '\0') {
4982 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4983 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4984 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4985 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4986 p++;
4989 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4990 ret = strcmp(buffer, expected_content_multi);
4991 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4993 DestroyWindow(hwnd);
4996 static void test_EM_GETTEXTLENGTHEX(void)
4998 HWND hwnd;
4999 GETTEXTLENGTHEX gtl;
5000 int ret;
5001 const char * base_string = "base string";
5002 const char * test_string = "a\nb\n\n\r\n";
5003 const char * test_string_after = "a";
5004 const char * test_string_2 = "a\rtest\rstring";
5005 char buffer[64] = {0};
5007 /* single line */
5008 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5009 0, 0, 200, 60, 0, 0, 0, 0);
5010 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5012 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5013 gtl.codepage = CP_ACP;
5014 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5015 ok(ret == 0, "ret %d\n",ret);
5017 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5018 gtl.codepage = CP_ACP;
5019 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5020 ok(ret == 0, "ret %d\n",ret);
5022 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
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 == strlen(base_string), "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 == strlen(base_string), "ret %d\n",ret);
5034 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_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 == 1, "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 == 1, "ret %d\n",ret);
5046 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5047 ret = strcmp(buffer, test_string_after);
5048 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5050 DestroyWindow(hwnd);
5052 /* multi line */
5053 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5054 0, 0, 200, 60, 0, 0, 0, 0);
5055 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5057 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5058 gtl.codepage = CP_ACP;
5059 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5060 ok(ret == 0, "ret %d\n",ret);
5062 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5063 gtl.codepage = CP_ACP;
5064 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5065 ok(ret == 0, "ret %d\n",ret);
5067 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5069 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5070 gtl.codepage = CP_ACP;
5071 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5072 ok(ret == strlen(base_string), "ret %d\n",ret);
5074 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5075 gtl.codepage = CP_ACP;
5076 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5077 ok(ret == strlen(base_string), "ret %d\n",ret);
5079 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5081 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5082 gtl.codepage = CP_ACP;
5083 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5084 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5086 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5087 gtl.codepage = CP_ACP;
5088 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5089 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5091 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5093 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5094 gtl.codepage = CP_ACP;
5095 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5096 ok(ret == 10, "ret %d\n",ret);
5098 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5099 gtl.codepage = CP_ACP;
5100 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5101 ok(ret == 6, "ret %d\n",ret);
5103 DestroyWindow(hwnd);
5107 /* globals that parent and child access when checking event masks & notifications */
5108 static HWND eventMaskEditHwnd = 0;
5109 static int queriedEventMask;
5110 static int watchForEventMask = 0;
5112 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5113 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5115 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5117 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5119 return DefWindowProcA(hwnd, message, wParam, lParam);
5122 /* test event masks in combination with WM_COMMAND */
5123 static void test_eventMask(void)
5125 HWND parent;
5126 int ret;
5127 WNDCLASSA cls;
5128 const char text[] = "foo bar\n";
5129 int eventMask;
5131 /* register class to capture WM_COMMAND */
5132 cls.style = 0;
5133 cls.lpfnWndProc = ParentMsgCheckProcA;
5134 cls.cbClsExtra = 0;
5135 cls.cbWndExtra = 0;
5136 cls.hInstance = GetModuleHandleA(0);
5137 cls.hIcon = 0;
5138 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5139 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5140 cls.lpszMenuName = NULL;
5141 cls.lpszClassName = "EventMaskParentClass";
5142 if(!RegisterClassA(&cls)) assert(0);
5144 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5145 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5146 ok (parent != 0, "Failed to create parent window\n");
5148 eventMaskEditHwnd = new_richedit(parent);
5149 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5151 eventMask = ENM_CHANGE | ENM_UPDATE;
5152 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5153 ok(ret == ENM_NONE, "wrong event mask\n");
5154 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5155 ok(ret == eventMask, "failed to set event mask\n");
5157 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5158 queriedEventMask = 0; /* initialize to something other than we expect */
5159 watchForEventMask = EN_CHANGE;
5160 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5161 ok(ret == TRUE, "failed to set text\n");
5162 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5163 notification in response to WM_SETTEXT */
5164 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5165 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5169 static int received_WM_NOTIFY = 0;
5170 static int modify_at_WM_NOTIFY = 0;
5171 static HWND hwndRichedit_WM_NOTIFY;
5173 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5175 if(message == WM_NOTIFY)
5177 received_WM_NOTIFY = 1;
5178 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5180 return DefWindowProcA(hwnd, message, wParam, lParam);
5183 static void test_WM_NOTIFY(void)
5185 HWND parent;
5186 WNDCLASSA cls;
5187 CHARFORMAT2 cf2;
5189 /* register class to capture WM_NOTIFY */
5190 cls.style = 0;
5191 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5192 cls.cbClsExtra = 0;
5193 cls.cbWndExtra = 0;
5194 cls.hInstance = GetModuleHandleA(0);
5195 cls.hIcon = 0;
5196 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5197 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5198 cls.lpszMenuName = NULL;
5199 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5200 if(!RegisterClassA(&cls)) assert(0);
5202 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5203 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5204 ok (parent != 0, "Failed to create parent window\n");
5206 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5207 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5209 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5211 /* Notifications for selection change should only be sent when selection
5212 actually changes. EM_SETCHARFORMAT is one message that calls
5213 ME_CommitUndo, which should check whether message should be sent */
5214 received_WM_NOTIFY = 0;
5215 cf2.cbSize = sizeof(CHARFORMAT2);
5216 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5217 (LPARAM) &cf2);
5218 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5219 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5220 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5221 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5223 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5224 already at 0. */
5225 received_WM_NOTIFY = 0;
5226 modify_at_WM_NOTIFY = 0;
5227 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5228 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5229 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5231 received_WM_NOTIFY = 0;
5232 modify_at_WM_NOTIFY = 0;
5233 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5234 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5236 received_WM_NOTIFY = 0;
5237 modify_at_WM_NOTIFY = 0;
5238 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5239 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5240 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5242 DestroyWindow(hwndRichedit_WM_NOTIFY);
5243 DestroyWindow(parent);
5246 static void test_undo_coalescing(void)
5248 HWND hwnd;
5249 int result;
5250 char buffer[64] = {0};
5252 /* multi-line control inserts CR normally */
5253 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5254 0, 0, 200, 60, 0, 0, 0, 0);
5255 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5257 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5258 ok (result == FALSE, "Can undo after window creation.\n");
5259 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5260 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5261 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5262 ok (result == FALSE, "Can redo after window creation.\n");
5263 result = SendMessage(hwnd, EM_REDO, 0, 0);
5264 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5266 /* Test the effect of arrows keys during typing on undo transactions*/
5267 simulate_typing_characters(hwnd, "one two three");
5268 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5269 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5270 simulate_typing_characters(hwnd, " four five six");
5272 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5273 ok (result == FALSE, "Can redo before anything is undone.\n");
5274 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5275 ok (result == TRUE, "Cannot undo typed characters.\n");
5276 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5277 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5278 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5279 ok (result == TRUE, "Cannot redo after undo.\n");
5280 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5281 result = strcmp(buffer, "one two three");
5282 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5284 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5285 ok (result == TRUE, "Cannot undo typed characters.\n");
5286 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5287 ok (result == TRUE, "Failed to undo typed characters.\n");
5288 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5289 result = strcmp(buffer, "");
5290 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5292 /* Test the effect of focus changes during typing on undo transactions*/
5293 simulate_typing_characters(hwnd, "one two three");
5294 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5295 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5296 SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
5297 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
5298 simulate_typing_characters(hwnd, " four five six");
5299 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5300 ok (result == TRUE, "Failed to undo typed characters.\n");
5301 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5302 result = strcmp(buffer, "one two three");
5303 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5305 /* Test the effect of the back key during typing on undo transactions */
5306 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5307 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5308 ok (result == TRUE, "Failed to clear the text.\n");
5309 simulate_typing_characters(hwnd, "one two threa");
5310 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5311 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5312 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5313 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5314 simulate_typing_characters(hwnd, "e four five six");
5315 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5316 ok (result == TRUE, "Failed to undo typed characters.\n");
5317 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5318 result = strcmp(buffer, "");
5319 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5321 /* Test the effect of the delete key during typing on undo transactions */
5322 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5323 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5324 ok(result == TRUE, "Failed to set the text.\n");
5325 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5326 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5327 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5328 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5329 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5330 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5331 ok (result == TRUE, "Failed to undo typed characters.\n");
5332 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5333 result = strcmp(buffer, "acd");
5334 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5335 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5336 ok (result == TRUE, "Failed to undo typed characters.\n");
5337 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5338 result = strcmp(buffer, "abcd");
5339 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5341 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5342 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5343 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5344 ok (result == TRUE, "Failed to clear the text.\n");
5345 simulate_typing_characters(hwnd, "one two three");
5346 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5347 ok (result == 0, "expected %d but got %d\n", 0, result);
5348 simulate_typing_characters(hwnd, " four five six");
5349 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5350 ok (result == TRUE, "Failed to undo typed characters.\n");
5351 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5352 result = strcmp(buffer, "one two three");
5353 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5354 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5355 ok (result == TRUE, "Failed to undo typed characters.\n");
5356 ok (result == TRUE, "Failed to undo typed characters.\n");
5357 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5358 result = strcmp(buffer, "");
5359 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5361 DestroyWindow(hwnd);
5364 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5365 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5367 static void test_word_movement(void)
5369 HWND hwnd;
5370 int result;
5371 int sel_start, sel_end;
5373 /* multi-line control inserts CR normally */
5374 hwnd = new_richedit(NULL);
5376 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5377 ok (result == TRUE, "Failed to clear the text.\n");
5378 SendMessage(hwnd, EM_SETSEL, 0, 0);
5379 /* |one two three */
5381 SEND_CTRL_RIGHT(hwnd);
5382 /* one |two three */
5383 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5384 ok(sel_start == sel_end, "Selection should be empty\n");
5385 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5387 SEND_CTRL_RIGHT(hwnd);
5388 /* one two |three */
5389 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5390 ok(sel_start == sel_end, "Selection should be empty\n");
5391 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5393 SEND_CTRL_LEFT(hwnd);
5394 /* one |two three */
5395 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5396 ok(sel_start == sel_end, "Selection should be empty\n");
5397 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5399 SEND_CTRL_LEFT(hwnd);
5400 /* |one two three */
5401 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5402 ok(sel_start == sel_end, "Selection should be empty\n");
5403 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5405 SendMessage(hwnd, EM_SETSEL, 8, 8);
5406 /* one two | three */
5407 SEND_CTRL_RIGHT(hwnd);
5408 /* one two |three */
5409 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5410 ok(sel_start == sel_end, "Selection should be empty\n");
5411 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5413 SendMessage(hwnd, EM_SETSEL, 11, 11);
5414 /* one two th|ree */
5415 SEND_CTRL_LEFT(hwnd);
5416 /* one two |three */
5417 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5418 ok(sel_start == sel_end, "Selection should be empty\n");
5419 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5421 DestroyWindow(hwnd);
5424 static void test_EM_CHARFROMPOS(void)
5426 HWND hwnd;
5427 int result;
5428 POINTL point;
5429 point.x = 0;
5430 point.y = 50;
5432 /* multi-line control inserts CR normally */
5433 hwnd = new_richedit(NULL);
5434 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5435 (LPARAM)"one two three four five six seven");
5437 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5438 ok(result == 0, "expected character index of 0 but got %d\n", result);
5440 DestroyWindow(hwnd);
5443 START_TEST( editor )
5445 MSG msg;
5446 time_t end;
5448 /* Must explicitly LoadLibrary(). The test has no references to functions in
5449 * RICHED20.DLL, so the linker doesn't actually link to it. */
5450 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
5451 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
5452 test_WM_CHAR();
5453 test_EM_FINDTEXT();
5454 test_EM_GETLINE();
5455 test_EM_POSFROMCHAR();
5456 test_EM_SCROLLCARET();
5457 test_EM_SCROLL();
5458 test_scrollbar_visibility();
5459 test_WM_SETTEXT();
5460 test_EM_LINELENGTH();
5461 test_EM_SETCHARFORMAT();
5462 test_EM_SETTEXTMODE();
5463 test_TM_PLAINTEXT();
5464 test_EM_SETOPTIONS();
5465 test_WM_GETTEXT();
5466 test_EM_GETTEXTRANGE();
5467 test_EM_GETSELTEXT();
5468 test_EM_SETUNDOLIMIT();
5469 test_ES_PASSWORD();
5470 test_EM_SETTEXTEX();
5471 test_EM_LIMITTEXT();
5472 test_EM_EXLIMITTEXT();
5473 test_EM_GETLIMITTEXT();
5474 test_WM_SETFONT();
5475 test_EM_GETMODIFY();
5476 test_EM_EXSETSEL();
5477 test_WM_PASTE();
5478 test_EM_STREAMIN();
5479 test_EM_STREAMOUT();
5480 test_EM_StreamIn_Undo();
5481 test_EM_FORMATRANGE();
5482 test_unicode_conversions();
5483 test_EM_GETTEXTLENGTHEX();
5484 test_EM_REPLACESEL(1);
5485 test_EM_REPLACESEL(0);
5486 test_WM_NOTIFY();
5487 test_EM_AUTOURLDETECT();
5488 test_eventMask();
5489 test_undo_coalescing();
5490 test_word_movement();
5491 test_EM_CHARFROMPOS();
5492 test_SETPARAFORMAT();
5494 /* Set the environment variable WINETEST_RICHED20 to keep windows
5495 * responsive and open for 30 seconds. This is useful for debugging.
5497 * The message pump uses PeekMessage() to empty the queue and then sleeps for
5498 * 50ms before retrying the queue. */
5499 end = time(NULL) + 30;
5500 if (getenv( "WINETEST_RICHED20" )) {
5501 while (time(NULL) < end) {
5502 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
5503 TranslateMessage(&msg);
5504 DispatchMessage(&msg);
5505 } else {
5506 Sleep(50);
5511 OleFlushClipboard();
5512 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());