push e2dd5002080a3a5df525dea14b261213385de3ae
[wine/hacks.git] / dlls / riched20 / tests / editor.c
blobe035b63a5e7e7c4cab08c3045619376ac5117f3c
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 /* Keeps the window reponsive for the deley_time in seconds.
61 * This is useful for debugging a test to see what is happening. */
62 void keep_responsive(time_t delay_time)
64 MSG msg;
65 time_t end;
67 /* The message pump uses PeekMessage() to empty the queue and then
68 * sleeps for 50ms before retrying the queue. */
69 end = time(NULL) + delay_time;
70 while (time(NULL) < end) {
71 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
72 TranslateMessage(&msg);
73 DispatchMessage(&msg);
74 } else {
75 Sleep(50);
80 static void processPendingMessages(void)
82 MSG msg;
83 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
84 TranslateMessage(&msg);
85 DispatchMessage(&msg);
89 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
91 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
92 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
93 SetFocus(hwnd);
94 keybd_event(mod_vk, mod_scan_code, 0, 0);
95 keybd_event(vk, scan_code, 0, 0);
96 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
97 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
98 processPendingMessages();
101 static void simulate_typing_characters(HWND hwnd, const char* szChars)
103 int ret;
105 while (*szChars != '\0') {
106 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
107 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
108 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
109 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
110 szChars++;
114 static const char haystack[] = "WINEWine wineWine wine WineWine";
115 /* ^0 ^10 ^20 ^30 */
117 struct find_s {
118 int start;
119 int end;
120 const char *needle;
121 int flags;
122 int expected_loc;
123 int _todo_wine;
127 struct find_s find_tests[] = {
128 /* Find in empty text */
129 {0, -1, "foo", FR_DOWN, -1, 0},
130 {0, -1, "foo", 0, -1, 0},
131 {0, -1, "", FR_DOWN, -1, 0},
132 {20, 5, "foo", FR_DOWN, -1, 0},
133 {5, 20, "foo", FR_DOWN, -1, 0}
136 struct find_s find_tests2[] = {
137 /* No-result find */
138 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
139 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
141 /* Subsequent finds */
142 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
143 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
144 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
145 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
147 /* Find backwards */
148 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
149 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
150 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
152 /* Case-insensitive */
153 {1, 31, "wInE", FR_DOWN, 4, 0},
154 {1, 31, "Wine", FR_DOWN, 4, 0},
156 /* High-to-low ranges */
157 {20, 5, "Wine", FR_DOWN, -1, 0},
158 {2, 1, "Wine", FR_DOWN, -1, 0},
159 {30, 29, "Wine", FR_DOWN, -1, 0},
160 {20, 5, "Wine", 0, 13, 0},
162 /* Find nothing */
163 {5, 10, "", FR_DOWN, -1, 0},
164 {10, 5, "", FR_DOWN, -1, 0},
165 {0, -1, "", FR_DOWN, -1, 0},
166 {10, 5, "", 0, -1, 0},
168 /* Whole-word search */
169 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
170 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
171 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
172 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
173 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
174 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
175 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
177 /* Bad ranges */
178 {5, 200, "XXX", FR_DOWN, -1, 0},
179 {-20, 20, "Wine", FR_DOWN, -1, 0},
180 {-20, 20, "Wine", FR_DOWN, -1, 0},
181 {-15, -20, "Wine", FR_DOWN, -1, 0},
182 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
184 /* Check the case noted in bug 4479 where matches at end aren't recognized */
185 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
186 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
187 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
188 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
189 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
191 /* The backwards case of bug 4479; bounds look right
192 * Fails because backward find is wrong */
193 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
194 {0, 20, "WINE", FR_MATCHCASE, -1, 0},
196 {0, -1, "wineWine wine", 0, -1, 0},
199 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
200 int findloc;
201 FINDTEXT ft;
202 memset(&ft, 0, sizeof(ft));
203 ft.chrg.cpMin = f->start;
204 ft.chrg.cpMax = f->end;
205 ft.lpstrText = f->needle;
206 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
207 ok(findloc == f->expected_loc,
208 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
209 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
212 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
213 int id) {
214 int findloc;
215 FINDTEXTEX ft;
216 int expected_end_loc;
218 memset(&ft, 0, sizeof(ft));
219 ft.chrg.cpMin = f->start;
220 ft.chrg.cpMax = f->end;
221 ft.lpstrText = f->needle;
222 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
223 ok(findloc == f->expected_loc,
224 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
225 name, id, f->needle, f->start, f->end, f->flags, findloc);
226 ok(ft.chrgText.cpMin == f->expected_loc,
227 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
228 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
229 expected_end_loc = ((f->expected_loc == -1) ? -1
230 : f->expected_loc + strlen(f->needle));
231 ok(ft.chrgText.cpMax == expected_end_loc,
232 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
233 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
236 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
237 int num_tests)
239 int i;
241 for (i = 0; i < num_tests; i++) {
242 if (find[i]._todo_wine) {
243 todo_wine {
244 check_EM_FINDTEXT(hwnd, name, &find[i], i);
245 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
247 } else {
248 check_EM_FINDTEXT(hwnd, name, &find[i], i);
249 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
254 static void test_EM_FINDTEXT(void)
256 HWND hwndRichEdit = new_richedit(NULL);
257 CHARFORMAT2 cf2;
259 /* Empty rich edit control */
260 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
261 sizeof(find_tests)/sizeof(struct find_s));
263 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
265 /* Haystack text */
266 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
267 sizeof(find_tests2)/sizeof(struct find_s));
269 /* Setting a format on an arbitrary range should have no effect in search
270 results. This tests correct offset reporting across runs. */
271 cf2.cbSize = sizeof(CHARFORMAT2);
272 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
273 (LPARAM) &cf2);
274 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
275 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
276 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
277 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
279 /* Haystack text, again */
280 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
281 sizeof(find_tests2)/sizeof(struct find_s));
283 /* Yet another range */
284 cf2.dwMask = CFM_BOLD | cf2.dwMask;
285 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
286 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
287 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
289 /* Haystack text, again */
290 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
291 sizeof(find_tests2)/sizeof(struct find_s));
293 DestroyWindow(hwndRichEdit);
296 static const struct getline_s {
297 int line;
298 size_t buffer_len;
299 const char *text;
300 } gl[] = {
301 {0, 10, "foo bar\r"},
302 {1, 10, "\r"},
303 {2, 10, "bar\r"},
304 {3, 10, "\r"},
306 /* Buffer smaller than line length */
307 {0, 2, "foo bar\r"},
308 {0, 1, "foo bar\r"},
309 {0, 0, "foo bar\r"}
312 static void test_EM_GETLINE(void)
314 int i;
315 HWND hwndRichEdit = new_richedit(NULL);
316 static const int nBuf = 1024;
317 char dest[1024], origdest[1024];
318 const char text[] = "foo bar\n"
319 "\n"
320 "bar\n";
322 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
324 memset(origdest, 0xBB, nBuf);
325 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
327 int nCopied;
328 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
329 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
330 memset(dest, 0xBB, nBuf);
331 *(WORD *) dest = gl[i].buffer_len;
333 /* EM_GETLINE appends a "\r\0" to the end of the line
334 * nCopied counts up to and including the '\r' */
335 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
336 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
337 expected_nCopied);
338 /* two special cases since a parameter is passed via dest */
339 if (gl[i].buffer_len == 0)
340 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
341 "buffer_len=0\n");
342 else if (gl[i].buffer_len == 1)
343 ok(dest[0] == gl[i].text[0] && !dest[1] &&
344 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
345 else
347 /* Prepare hex strings of buffers to dump on failure. */
348 char expectedbuf[1024];
349 char resultbuf[1024];
350 int j;
351 resultbuf[0] = '\0';
352 for (j = 0; j < 32; j++)
353 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
354 expectedbuf[0] = '\0';
355 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
356 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
357 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
358 sprintf(expectedbuf+strlen(expectedbuf), "??");
359 for (; j < 32; j++) /* Bytes after declared buffer size */
360 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
362 /* Test the part of the buffer that is expected to be written according
363 * to the MSDN documentation fo EM_GETLINE, which does not state that
364 * a NULL terminating character will be added unless no text is copied.
366 * Windows 95, 98 & NT do not append a NULL terminating character, but
367 * Windows 2000 and up do append a NULL terminating character if there
368 * is space in the buffer. The test will ignore this difference. */
369 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
370 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
371 i, expected_bytes_written, expectedbuf, resultbuf);
372 /* Test the part of the buffer after the declared length to make sure
373 * there are no buffer overruns. */
374 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
375 nBuf - gl[i].buffer_len),
376 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
377 i, expected_bytes_written, expectedbuf, resultbuf);
381 DestroyWindow(hwndRichEdit);
384 static void test_EM_LINELENGTH(void)
386 HWND hwndRichEdit = new_richedit(NULL);
387 const char * text =
388 "richedit1\r"
389 "richedit1\n"
390 "richedit1\r\n"
391 "richedit1";
392 int offset_test[10][2] = {
393 {0, 9},
394 {5, 9},
395 {10, 9},
396 {15, 9},
397 {20, 9},
398 {25, 9},
399 {30, 9},
400 {35, 9},
401 {40, 0},
402 {45, 0},
404 int i;
405 LRESULT result;
407 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
409 for (i = 0; i < 10; i++) {
410 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
411 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
412 offset_test[i][0], result, offset_test[i][1]);
415 DestroyWindow(hwndRichEdit);
418 static int get_scroll_pos_y(HWND hwnd)
420 POINT p = {-1, -1};
421 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
422 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
423 return p.y;
426 static void move_cursor(HWND hwnd, long charindex)
428 CHARRANGE cr;
429 cr.cpMax = charindex;
430 cr.cpMin = charindex;
431 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
434 static void line_scroll(HWND hwnd, int amount)
436 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
439 static void test_EM_SCROLLCARET(void)
441 int prevY, curY;
442 const char text[] = "aa\n"
443 "this is a long line of text that should be longer than the "
444 "control's width\n"
445 "cc\n"
446 "dd\n"
447 "ee\n"
448 "ff\n"
449 "gg\n"
450 "hh\n";
451 /* The richedit window height needs to be large enough vertically to fit in
452 * more than two lines of text, so the new_richedit function can't be used
453 * since a height of 60 was not large enough on some systems.
455 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
456 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
457 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
458 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
460 /* Can't verify this */
461 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
463 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
465 /* Caret above visible window */
466 line_scroll(hwndRichEdit, 3);
467 prevY = get_scroll_pos_y(hwndRichEdit);
468 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
469 curY = get_scroll_pos_y(hwndRichEdit);
470 ok(prevY != curY, "%d == %d\n", prevY, curY);
472 /* Caret below visible window */
473 move_cursor(hwndRichEdit, sizeof(text) - 1);
474 line_scroll(hwndRichEdit, -3);
475 prevY = get_scroll_pos_y(hwndRichEdit);
476 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
477 curY = get_scroll_pos_y(hwndRichEdit);
478 ok(prevY != curY, "%d == %d\n", prevY, curY);
480 /* Caret in visible window */
481 move_cursor(hwndRichEdit, sizeof(text) - 2);
482 prevY = get_scroll_pos_y(hwndRichEdit);
483 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
484 curY = get_scroll_pos_y(hwndRichEdit);
485 ok(prevY == curY, "%d != %d\n", prevY, curY);
487 /* Caret still in visible window */
488 line_scroll(hwndRichEdit, -1);
489 prevY = get_scroll_pos_y(hwndRichEdit);
490 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
491 curY = get_scroll_pos_y(hwndRichEdit);
492 ok(prevY == curY, "%d != %d\n", prevY, curY);
494 DestroyWindow(hwndRichEdit);
497 static void test_EM_POSFROMCHAR(void)
499 HWND hwndRichEdit = new_richedit(NULL);
500 int i;
501 LRESULT result;
502 unsigned int height = 0;
503 int xpos = 0;
504 static const char text[] = "aa\n"
505 "this is a long line of text that should be longer than the "
506 "control's width\n"
507 "cc\n"
508 "dd\n"
509 "ee\n"
510 "ff\n"
511 "gg\n"
512 "hh\n";
514 /* Fill the control to lines to ensure that most of them are offscreen */
515 for (i = 0; i < 50; i++)
517 /* Do not modify the string; it is exactly 16 characters long. */
518 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
519 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
523 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
524 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
525 Richedit 3.0 accepts either of the above API conventions.
528 /* Testing Richedit 2.0 API format */
530 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
531 Since all lines are identical and drawn with the same font,
532 they should have the same height... right?
534 for (i = 0; i < 50; i++)
536 /* All the lines are 16 characters long */
537 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
538 if (i == 0)
540 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
541 todo_wine {
542 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
544 xpos = LOWORD(result);
546 else if (i == 1)
548 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
549 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
550 height = HIWORD(result);
552 else
554 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
555 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
559 /* Testing position at end of text */
560 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
561 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
562 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
564 /* Testing position way past end of text */
565 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
566 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
567 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
569 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
570 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
571 for (i = 0; i < 50; i++)
573 /* All the lines are 16 characters long */
574 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
575 ok((signed short)(HIWORD(result)) == (i - 1) * height,
576 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
577 (signed short)(HIWORD(result)), (i - 1) * height);
578 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
581 /* Testing position at end of text */
582 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
583 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
584 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
586 /* Testing position way past end of text */
587 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
588 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
589 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
591 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
592 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
593 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
595 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
596 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
597 todo_wine {
598 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
600 xpos = LOWORD(result);
602 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
603 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
604 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
605 todo_wine {
606 /* Fails on builtin because horizontal scrollbar is not being shown */
607 ok((signed short)(LOWORD(result)) < xpos,
608 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
609 (signed short)(LOWORD(result)), xpos);
611 DestroyWindow(hwndRichEdit);
614 static void test_EM_SETCHARFORMAT(void)
616 HWND hwndRichEdit = new_richedit(NULL);
617 CHARFORMAT2 cf2;
618 int rc = 0;
619 int tested_effects[] = {
620 CFE_BOLD,
621 CFE_ITALIC,
622 CFE_UNDERLINE,
623 CFE_STRIKEOUT,
624 CFE_PROTECTED,
625 CFE_LINK,
626 CFE_SUBSCRIPT,
627 CFE_SUPERSCRIPT,
630 int i;
631 CHARRANGE cr;
633 /* Invalid flags, CHARFORMAT2 structure blanked out */
634 memset(&cf2, 0, sizeof(cf2));
635 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
636 (LPARAM) &cf2);
637 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
639 /* A valid flag, CHARFORMAT2 structure blanked out */
640 memset(&cf2, 0, sizeof(cf2));
641 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
642 (LPARAM) &cf2);
643 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
645 /* A valid flag, CHARFORMAT2 structure blanked out */
646 memset(&cf2, 0, sizeof(cf2));
647 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
648 (LPARAM) &cf2);
649 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
651 /* A valid flag, CHARFORMAT2 structure blanked out */
652 memset(&cf2, 0, sizeof(cf2));
653 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
654 (LPARAM) &cf2);
655 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
657 /* A valid flag, CHARFORMAT2 structure blanked out */
658 memset(&cf2, 0, sizeof(cf2));
659 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
660 (LPARAM) &cf2);
661 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
663 /* Invalid flags, CHARFORMAT2 structure minimally filled */
664 memset(&cf2, 0, sizeof(cf2));
665 cf2.cbSize = sizeof(CHARFORMAT2);
666 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
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_DEFAULT,
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 ok(rc == FALSE, "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_SELECTION,
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 ok(rc == FALSE, "Should not be able to undo here.\n");
691 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
693 /* A valid flag, CHARFORMAT2 structure minimally filled */
694 memset(&cf2, 0, sizeof(cf2));
695 cf2.cbSize = sizeof(CHARFORMAT2);
696 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
697 (LPARAM) &cf2);
698 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
699 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
700 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
701 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
703 /* A valid flag, CHARFORMAT2 structure minimally filled */
704 memset(&cf2, 0, sizeof(cf2));
705 cf2.cbSize = sizeof(CHARFORMAT2);
706 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
707 (LPARAM) &cf2);
708 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
709 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
710 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
711 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
713 cf2.cbSize = sizeof(CHARFORMAT2);
714 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
715 (LPARAM) &cf2);
717 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
718 cf2.cbSize = sizeof(CHARFORMAT2);
719 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
720 (LPARAM) &cf2);
721 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
722 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
724 /* wParam==0 is default char format, does not set modify */
725 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
726 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
727 ok(rc == 0, "Text marked as modified, expected not modified!\n");
728 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
729 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
730 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
731 ok(rc == 0, "Text marked as modified, expected not modified!\n");
733 /* wParam==SCF_SELECTION sets modify if nonempty selection */
734 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
735 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
736 ok(rc == 0, "Text marked as modified, expected not modified!\n");
737 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
738 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
739 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
740 ok(rc == 0, "Text marked as modified, expected not modified!\n");
742 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
743 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
744 ok(rc == 0, "Text marked as modified, expected not modified!\n");
745 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
746 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
747 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
748 ok(rc == 0, "Text marked as modified, expected not modified!\n");
749 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
750 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
751 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
752 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
753 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
755 /* wParam==SCF_ALL sets modify regardless of whether text is present */
756 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
757 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
758 ok(rc == 0, "Text marked as modified, expected not modified!\n");
759 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
760 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
761 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
762 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
764 DestroyWindow(hwndRichEdit);
766 /* EM_GETCHARFORMAT tests */
767 for (i = 0; tested_effects[i]; i++)
769 hwndRichEdit = new_richedit(NULL);
770 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
772 /* Need to set a TrueType font to get consistent CFM_BOLD results */
773 memset(&cf2, 0, sizeof(CHARFORMAT2));
774 cf2.cbSize = sizeof(CHARFORMAT2);
775 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
776 cf2.dwEffects = 0;
777 strcpy(cf2.szFaceName, "Courier New");
778 cf2.wWeight = FW_DONTCARE;
779 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
781 memset(&cf2, 0, sizeof(CHARFORMAT2));
782 cf2.cbSize = sizeof(CHARFORMAT2);
783 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
784 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
785 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
786 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
788 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
789 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
790 ok((cf2.dwEffects & tested_effects[i]) == 0,
791 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
793 memset(&cf2, 0, sizeof(CHARFORMAT2));
794 cf2.cbSize = sizeof(CHARFORMAT2);
795 cf2.dwMask = tested_effects[i];
796 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
797 cf2.dwMask = CFM_SUPERSCRIPT;
798 cf2.dwEffects = tested_effects[i];
799 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
800 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
802 memset(&cf2, 0, sizeof(CHARFORMAT2));
803 cf2.cbSize = sizeof(CHARFORMAT2);
804 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
805 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
806 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
807 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
809 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
810 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
811 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
812 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
814 memset(&cf2, 0, sizeof(CHARFORMAT2));
815 cf2.cbSize = sizeof(CHARFORMAT2);
816 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
817 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
818 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
819 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
821 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
822 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
823 ok((cf2.dwEffects & tested_effects[i]) == 0,
824 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
826 memset(&cf2, 0, sizeof(CHARFORMAT2));
827 cf2.cbSize = sizeof(CHARFORMAT2);
828 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
829 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
830 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
831 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
833 (cf2.dwMask & tested_effects[i]) == 0),
834 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
836 DestroyWindow(hwndRichEdit);
839 for (i = 0; tested_effects[i]; i++)
841 hwndRichEdit = new_richedit(NULL);
842 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
844 /* Need to set a TrueType font to get consistent CFM_BOLD results */
845 memset(&cf2, 0, sizeof(CHARFORMAT2));
846 cf2.cbSize = sizeof(CHARFORMAT2);
847 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
848 cf2.dwEffects = 0;
849 strcpy(cf2.szFaceName, "Courier New");
850 cf2.wWeight = FW_DONTCARE;
851 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
853 memset(&cf2, 0, sizeof(CHARFORMAT2));
854 cf2.cbSize = sizeof(CHARFORMAT2);
855 cf2.dwMask = tested_effects[i];
856 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
857 cf2.dwMask = CFM_SUPERSCRIPT;
858 cf2.dwEffects = tested_effects[i];
859 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
860 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
862 memset(&cf2, 0, sizeof(CHARFORMAT2));
863 cf2.cbSize = sizeof(CHARFORMAT2);
864 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
865 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
866 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
867 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
869 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
870 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
871 ok((cf2.dwEffects & tested_effects[i]) == 0,
872 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
874 memset(&cf2, 0, sizeof(CHARFORMAT2));
875 cf2.cbSize = sizeof(CHARFORMAT2);
876 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
877 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
878 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
879 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
881 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
882 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
883 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
884 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
886 memset(&cf2, 0, sizeof(CHARFORMAT2));
887 cf2.cbSize = sizeof(CHARFORMAT2);
888 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
889 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
890 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
891 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
893 (cf2.dwMask & tested_effects[i]) == 0),
894 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
895 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
896 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
898 DestroyWindow(hwndRichEdit);
901 /* Effects applied on an empty selection should take effect when selection is
902 replaced with text */
903 hwndRichEdit = new_richedit(NULL);
904 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
905 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
907 memset(&cf2, 0, sizeof(CHARFORMAT2));
908 cf2.cbSize = sizeof(CHARFORMAT2);
909 cf2.dwMask = CFM_BOLD;
910 cf2.dwEffects = CFE_BOLD;
911 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
913 /* Selection is now nonempty */
914 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
916 memset(&cf2, 0, sizeof(CHARFORMAT2));
917 cf2.cbSize = sizeof(CHARFORMAT2);
918 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
919 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
921 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
922 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
923 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
924 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
927 /* Set two effects on an empty selection */
928 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
929 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
931 memset(&cf2, 0, sizeof(CHARFORMAT2));
932 cf2.cbSize = sizeof(CHARFORMAT2);
933 cf2.dwMask = CFM_BOLD;
934 cf2.dwEffects = CFE_BOLD;
935 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
936 cf2.dwMask = CFM_ITALIC;
937 cf2.dwEffects = CFE_ITALIC;
938 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
940 /* Selection is now nonempty */
941 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
943 memset(&cf2, 0, sizeof(CHARFORMAT2));
944 cf2.cbSize = sizeof(CHARFORMAT2);
945 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
946 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
948 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
949 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
950 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
951 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
953 /* Setting the (empty) selection to exactly the same place as before should
954 NOT clear the insertion style! */
955 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
956 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
958 memset(&cf2, 0, sizeof(CHARFORMAT2));
959 cf2.cbSize = sizeof(CHARFORMAT2);
960 cf2.dwMask = CFM_BOLD;
961 cf2.dwEffects = CFE_BOLD;
962 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
964 /* Empty selection in same place, insert style should NOT be forgotten here. */
965 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
967 /* Selection is now nonempty */
968 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
970 memset(&cf2, 0, sizeof(CHARFORMAT2));
971 cf2.cbSize = sizeof(CHARFORMAT2);
972 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
973 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
975 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
976 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
977 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
978 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
980 /* Ditto with EM_EXSETSEL */
981 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
982 cr.cpMin = 2; cr.cpMax = 2;
983 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
985 memset(&cf2, 0, sizeof(CHARFORMAT2));
986 cf2.cbSize = sizeof(CHARFORMAT2);
987 cf2.dwMask = CFM_BOLD;
988 cf2.dwEffects = CFE_BOLD;
989 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
991 /* Empty selection in same place, insert style should NOT be forgotten here. */
992 cr.cpMin = 2; cr.cpMax = 2;
993 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
995 /* Selection is now nonempty */
996 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
998 memset(&cf2, 0, sizeof(CHARFORMAT2));
999 cf2.cbSize = sizeof(CHARFORMAT2);
1000 cr.cpMin = 2; cr.cpMax = 6;
1001 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1002 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1004 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1005 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1006 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1007 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1009 DestroyWindow(hwndRichEdit);
1012 static void test_EM_SETTEXTMODE(void)
1014 HWND hwndRichEdit = new_richedit(NULL);
1015 CHARFORMAT2 cf2, cf2test;
1016 CHARRANGE cr;
1017 int rc = 0;
1019 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1020 /*Insert text into the control*/
1022 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1024 /*Attempt to change the control to plain text mode*/
1025 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1026 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1028 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1029 If rich text is pasted, it should have the same formatting as the rest
1030 of the text in the control*/
1032 /*Italicize the text
1033 *NOTE: If the default text was already italicized, the test will simply
1034 reverse; in other words, it will copy a regular "wine" into a plain
1035 text window that uses an italicized format*/
1036 cf2.cbSize = sizeof(CHARFORMAT2);
1037 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1038 (LPARAM) &cf2);
1040 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1041 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1043 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1044 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1046 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1047 however, SCF_ALL has been implemented*/
1048 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1049 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1051 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1052 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1054 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1056 /*Select the string "wine"*/
1057 cr.cpMin = 0;
1058 cr.cpMax = 4;
1059 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1061 /*Copy the italicized "wine" to the clipboard*/
1062 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1064 /*Reset the formatting to default*/
1065 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1066 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1067 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1069 /*Clear the text in the control*/
1070 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1072 /*Switch to Plain Text Mode*/
1073 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1074 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1076 /*Input "wine" again in normal format*/
1077 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1079 /*Paste the italicized "wine" into the control*/
1080 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1082 /*Select a character from the first "wine" string*/
1083 cr.cpMin = 2;
1084 cr.cpMax = 3;
1085 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1087 /*Retrieve its formatting*/
1088 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1089 (LPARAM) &cf2);
1091 /*Select a character from the second "wine" string*/
1092 cr.cpMin = 5;
1093 cr.cpMax = 6;
1094 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1096 /*Retrieve its formatting*/
1097 cf2test.cbSize = sizeof(CHARFORMAT2);
1098 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1099 (LPARAM) &cf2test);
1101 /*Compare the two formattings*/
1102 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1103 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1104 cf2.dwEffects, cf2test.dwEffects);
1105 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1106 printing "wine" in the current format(normal)
1107 pasting "wine" from the clipboard(italicized)
1108 comparing the two formats(should differ)*/
1110 /*Attempt to switch with text in control*/
1111 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1112 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1114 /*Clear control*/
1115 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1117 /*Switch into Rich Text mode*/
1118 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1119 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1121 /*Print "wine" in normal formatting into the control*/
1122 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1124 /*Paste italicized "wine" into the control*/
1125 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1127 /*Select text from the first "wine" string*/
1128 cr.cpMin = 1;
1129 cr.cpMax = 3;
1130 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1132 /*Retrieve its formatting*/
1133 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1134 (LPARAM) &cf2);
1136 /*Select text from the second "wine" string*/
1137 cr.cpMin = 6;
1138 cr.cpMax = 7;
1139 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1141 /*Retrieve its formatting*/
1142 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1143 (LPARAM) &cf2test);
1145 /*Test that the two formattings are not the same*/
1146 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1147 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1148 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1150 DestroyWindow(hwndRichEdit);
1153 static void test_SETPARAFORMAT(void)
1155 HWND hwndRichEdit = new_richedit(NULL);
1156 PARAFORMAT2 fmt;
1157 HRESULT ret;
1158 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1159 fmt.cbSize = sizeof(PARAFORMAT2);
1160 fmt.dwMask = PFM_ALIGNMENT;
1161 fmt.wAlignment = PFA_LEFT;
1163 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1164 ok(ret != 0, "expected non-zero got %d\n", ret);
1166 fmt.cbSize = sizeof(PARAFORMAT2);
1167 fmt.dwMask = -1;
1168 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1169 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1170 * between richedit different native builds of riched20.dll
1171 * used on different Windows versions. */
1172 ret &= ~PFM_TABLEROWDELIMITER;
1173 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1175 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1176 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1178 DestroyWindow(hwndRichEdit);
1181 static void test_TM_PLAINTEXT(void)
1183 /*Tests plain text properties*/
1185 HWND hwndRichEdit = new_richedit(NULL);
1186 CHARFORMAT2 cf2, cf2test;
1187 CHARRANGE cr;
1188 int rc = 0;
1190 /*Switch to plain text mode*/
1192 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1193 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1195 /*Fill control with text*/
1197 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1199 /*Select some text and bold it*/
1201 cr.cpMin = 10;
1202 cr.cpMax = 20;
1203 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1204 cf2.cbSize = sizeof(CHARFORMAT2);
1205 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1206 (LPARAM) &cf2);
1208 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1209 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1211 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1212 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1214 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1215 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1217 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1218 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1220 /*Get the formatting of those characters*/
1222 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1224 /*Get the formatting of some other characters*/
1225 cf2test.cbSize = sizeof(CHARFORMAT2);
1226 cr.cpMin = 21;
1227 cr.cpMax = 30;
1228 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1229 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1231 /*Test that they are the same as plain text allows only one formatting*/
1233 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1234 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1235 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1237 /*Fill the control with a "wine" string, which when inserted will be bold*/
1239 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1241 /*Copy the bolded "wine" string*/
1243 cr.cpMin = 0;
1244 cr.cpMax = 4;
1245 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1246 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1248 /*Swap back to rich text*/
1250 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1251 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1253 /*Set the default formatting to bold italics*/
1255 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1256 cf2.dwMask |= CFM_ITALIC;
1257 cf2.dwEffects ^= CFE_ITALIC;
1258 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1259 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1261 /*Set the text in the control to "wine", which will be bold and italicized*/
1263 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1265 /*Paste the plain text "wine" string, which should take the insert
1266 formatting, which at the moment is bold italics*/
1268 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1270 /*Select the first "wine" string and retrieve its formatting*/
1272 cr.cpMin = 1;
1273 cr.cpMax = 3;
1274 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1275 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1277 /*Select the second "wine" string and retrieve its formatting*/
1279 cr.cpMin = 5;
1280 cr.cpMax = 7;
1281 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1282 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1284 /*Compare the two formattings. They should be the same.*/
1286 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1287 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1288 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1289 DestroyWindow(hwndRichEdit);
1292 static void test_WM_GETTEXT(void)
1294 HWND hwndRichEdit = new_richedit(NULL);
1295 static const char text[] = "Hello. My name is RichEdit!";
1296 static const char text2[] = "Hello. My name is RichEdit!\r";
1297 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1298 char buffer[1024] = {0};
1299 int result;
1301 /* Baseline test with normal-sized buffer */
1302 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1303 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1304 ok(result == lstrlen(buffer),
1305 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1306 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1307 result = strcmp(buffer,text);
1308 ok(result == 0,
1309 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1311 /* Test for returned value of WM_GETTEXTLENGTH */
1312 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1313 ok(result == lstrlen(text),
1314 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1315 result, lstrlen(text));
1317 /* Test for behavior in overflow case */
1318 memset(buffer, 0, 1024);
1319 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1320 ok(result == 0 ||
1321 result == lstrlenA(text) - 1, /* XP, win2k3 */
1322 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1323 result = strcmp(buffer,text);
1324 if (result)
1325 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1326 ok(result == 0,
1327 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1329 /* Baseline test with normal-sized buffer and carriage return */
1330 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1331 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1332 ok(result == lstrlen(buffer),
1333 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1334 result = strcmp(buffer,text2_after);
1335 ok(result == 0,
1336 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1338 /* Test for returned value of WM_GETTEXTLENGTH */
1339 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1340 ok(result == lstrlen(text2_after),
1341 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1342 result, lstrlen(text2_after));
1344 /* Test for behavior of CRLF conversion in case of overflow */
1345 memset(buffer, 0, 1024);
1346 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1347 ok(result == 0 ||
1348 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1349 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1350 result = strcmp(buffer,text2);
1351 if (result)
1352 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1353 ok(result == 0,
1354 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1356 DestroyWindow(hwndRichEdit);
1359 static void test_EM_GETTEXTRANGE(void)
1361 HWND hwndRichEdit = new_richedit(NULL);
1362 const char * text1 = "foo bar\r\nfoo bar";
1363 const char * text2 = "foo bar\rfoo bar";
1364 const char * expect = "bar\rfoo";
1365 char buffer[1024] = {0};
1366 LRESULT result;
1367 TEXTRANGEA textRange;
1369 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1371 textRange.lpstrText = buffer;
1372 textRange.chrg.cpMin = 4;
1373 textRange.chrg.cpMax = 11;
1374 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1375 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1376 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1378 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1380 textRange.lpstrText = buffer;
1381 textRange.chrg.cpMin = 4;
1382 textRange.chrg.cpMax = 11;
1383 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1384 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1385 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1387 DestroyWindow(hwndRichEdit);
1390 static void test_EM_GETSELTEXT(void)
1392 HWND hwndRichEdit = new_richedit(NULL);
1393 const char * text1 = "foo bar\r\nfoo bar";
1394 const char * text2 = "foo bar\rfoo bar";
1395 const char * expect = "bar\rfoo";
1396 char buffer[1024] = {0};
1397 LRESULT result;
1399 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1401 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1402 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1403 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1404 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1406 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1408 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1409 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1410 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1411 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1413 DestroyWindow(hwndRichEdit);
1416 /* FIXME: need to test unimplemented options and robustly test wparam */
1417 static void test_EM_SETOPTIONS(void)
1419 HWND hwndRichEdit = new_richedit(NULL);
1420 static const char text[] = "Hello. My name is RichEdit!";
1421 char buffer[1024] = {0};
1423 /* NEGATIVE TESTING - NO OPTIONS SET */
1424 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1425 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1427 /* testing no readonly by sending 'a' to the control*/
1428 SetFocus(hwndRichEdit);
1429 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1430 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1431 ok(buffer[0]=='a',
1432 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1433 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1435 /* READONLY - sending 'a' to the control */
1436 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1437 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1438 SetFocus(hwndRichEdit);
1439 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1440 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1441 ok(buffer[0]==text[0],
1442 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1444 DestroyWindow(hwndRichEdit);
1447 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1449 CHARFORMAT2W text_format;
1450 text_format.cbSize = sizeof(text_format);
1451 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1452 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1453 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1456 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1458 int link_present = 0;
1460 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1461 if (is_url)
1462 { /* control text is url; should get CFE_LINK */
1463 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1465 else
1467 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1471 static HWND new_static_wnd(HWND parent) {
1472 return new_window("Static", 0, parent);
1475 static void test_EM_AUTOURLDETECT(void)
1477 /* DO NOT change the properties of the first two elements. To shorten the
1478 tests, all tests after WM_SETTEXT test just the first two elements -
1479 one non-URL and one URL */
1480 struct urls_s {
1481 const char *text;
1482 int is_url;
1483 } urls[12] = {
1484 {"winehq.org", 0},
1485 {"http://www.winehq.org", 1},
1486 {"http//winehq.org", 0},
1487 {"ww.winehq.org", 0},
1488 {"www.winehq.org", 1},
1489 {"ftp://192.168.1.1", 1},
1490 {"ftp//192.168.1.1", 0},
1491 {"mailto:your@email.com", 1},
1492 {"prospero:prosperoserver", 1},
1493 {"telnet:test", 1},
1494 {"news:newserver", 1},
1495 {"wais:waisserver", 1}
1498 int i, j;
1499 int urlRet=-1;
1500 HWND hwndRichEdit, parent;
1502 /* All of the following should cause the URL to be detected */
1503 const char * templates_delim[] = {
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\r on it",
1507 "This is some text with ---X--- on it",
1508 "This is some text with \"X\" on it",
1509 "This is some text with 'X' on it",
1510 "This is some text with 'X' on it",
1511 "This is some text with :X: on it",
1513 "This text ends with X",
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",
1521 "This is some text with (X on it",
1522 "This is some text with \rX on it",
1523 "This is some text with ---X on it",
1524 "This is some text with \"X on it",
1525 "This is some text with 'X on it",
1526 "This is some text with :X on it",
1528 /* None of these should cause the URL to be detected */
1529 const char * templates_non_delim[] = {
1530 "This is some text with |X| on it",
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",
1539 "This is some text with *X on it",
1540 "This is some text with /X on it",
1541 "This is some text with +X on it",
1542 "This is some text with %X on it",
1543 "This is some text with #X on it",
1544 "This is some text with @X on it",
1545 "This is some text with \\X on it",
1547 /* All of these cause the URL detection to be extended by one more byte,
1548 thus demonstrating that the tested character is considered as part
1549 of the URL. */
1550 const char * templates_xten_delim[] = {
1551 "This is some text with X| on it",
1552 "This is some text with X* on it",
1553 "This is some text with X/ on it",
1554 "This is some text with X+ on it",
1555 "This is some text with X% on it",
1556 "This is some text with X# on it",
1557 "This is some text with X@ on it",
1558 "This is some text with X\\ on it",
1560 char buffer[1024];
1562 parent = new_static_wnd(NULL);
1563 hwndRichEdit = new_richedit(parent);
1564 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1565 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1566 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1567 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1568 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1569 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1570 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1571 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1572 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1573 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1574 /* for each url, check the text to see if CFE_LINK effect is present */
1575 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1577 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1578 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1579 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1581 /* Link detection should happen immediately upon WM_SETTEXT */
1582 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1583 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1584 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1586 DestroyWindow(hwndRichEdit);
1588 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1589 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1590 hwndRichEdit = new_richedit(parent);
1592 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1593 char * at_pos;
1594 int at_offset;
1595 int end_offset;
1597 at_pos = strchr(templates_delim[j], 'X');
1598 at_offset = at_pos - templates_delim[j];
1599 strncpy(buffer, templates_delim[j], at_offset);
1600 buffer[at_offset] = '\0';
1601 strcat(buffer, urls[i].text);
1602 strcat(buffer, templates_delim[j] + at_offset + 1);
1603 end_offset = at_offset + strlen(urls[i].text);
1605 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1606 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1608 /* This assumes no templates start with the URL itself, and that they
1609 have at least two characters before the URL text */
1610 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1611 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1612 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1613 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1614 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1615 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1617 if (urls[i].is_url)
1619 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1620 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1621 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1622 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1624 else
1626 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1627 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1628 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1629 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1631 if (buffer[end_offset] != '\0')
1633 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1634 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1635 if (buffer[end_offset +1] != '\0')
1637 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1638 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1643 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1644 char * at_pos;
1645 int at_offset;
1646 int end_offset;
1648 at_pos = strchr(templates_non_delim[j], 'X');
1649 at_offset = at_pos - templates_non_delim[j];
1650 strncpy(buffer, templates_non_delim[j], at_offset);
1651 buffer[at_offset] = '\0';
1652 strcat(buffer, urls[i].text);
1653 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1654 end_offset = at_offset + strlen(urls[i].text);
1656 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1657 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1659 /* This assumes no templates start with the URL itself, and that they
1660 have at least two characters before the URL text */
1661 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1662 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1663 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1664 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1665 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1666 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1668 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1669 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1670 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1671 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1672 if (buffer[end_offset] != '\0')
1674 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1675 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1676 if (buffer[end_offset +1] != '\0')
1678 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1679 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1684 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1685 char * at_pos;
1686 int at_offset;
1687 int end_offset;
1689 at_pos = strchr(templates_xten_delim[j], 'X');
1690 at_offset = at_pos - templates_xten_delim[j];
1691 strncpy(buffer, templates_xten_delim[j], at_offset);
1692 buffer[at_offset] = '\0';
1693 strcat(buffer, urls[i].text);
1694 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1695 end_offset = at_offset + strlen(urls[i].text);
1697 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1698 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1700 /* This assumes no templates start with the URL itself, and that they
1701 have at least two characters before the URL text */
1702 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1703 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1704 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1705 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1706 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1707 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1709 if (urls[i].is_url)
1711 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1712 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1713 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1714 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1715 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1716 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1718 else
1720 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1721 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1722 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1723 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1724 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1725 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1727 if (buffer[end_offset +1] != '\0')
1729 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1730 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1731 if (buffer[end_offset +2] != '\0')
1733 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1734 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1739 DestroyWindow(hwndRichEdit);
1740 hwndRichEdit = NULL;
1743 /* Test detection of URLs within normal text - WM_CHAR case. */
1744 /* Test only the first two URL examples for brevity */
1745 for (i = 0; i < 2; i++) {
1746 hwndRichEdit = new_richedit(parent);
1748 /* Also for brevity, test only the first three delimiters */
1749 for (j = 0; j < 3; j++) {
1750 char * at_pos;
1751 int at_offset;
1752 int end_offset;
1753 int u, v;
1755 at_pos = strchr(templates_delim[j], 'X');
1756 at_offset = at_pos - templates_delim[j];
1757 end_offset = at_offset + strlen(urls[i].text);
1759 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1760 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1761 for (u = 0; templates_delim[j][u]; u++) {
1762 if (templates_delim[j][u] == '\r') {
1763 simulate_typing_characters(hwndRichEdit, "\r");
1764 } else if (templates_delim[j][u] != 'X') {
1765 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1766 } else {
1767 for (v = 0; urls[i].text[v]; v++) {
1768 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1772 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1774 /* This assumes no templates start with the URL itself, and that they
1775 have at least two characters before the URL text */
1776 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1777 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1778 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1779 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1780 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1781 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1783 if (urls[i].is_url)
1785 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1786 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1787 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1788 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1790 else
1792 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1793 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1794 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1795 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1797 if (buffer[end_offset] != '\0')
1799 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1800 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1801 if (buffer[end_offset +1] != '\0')
1803 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1804 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1808 /* The following will insert a paragraph break after the first character
1809 of the URL candidate, thus breaking the URL. It is expected that the
1810 CFE_LINK attribute should break across both pieces of the URL */
1811 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1812 simulate_typing_characters(hwndRichEdit, "\r");
1813 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1815 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1816 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1817 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1818 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1819 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1820 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1822 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1823 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1824 /* end_offset moved because of paragraph break */
1825 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1826 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1827 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1828 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1830 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1831 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1832 if (buffer[end_offset +2] != '\0')
1834 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1835 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1839 /* The following will remove the just-inserted paragraph break, thus
1840 restoring the URL */
1841 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1842 simulate_typing_characters(hwndRichEdit, "\b");
1843 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1845 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1846 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1847 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1848 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1849 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1850 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1852 if (urls[i].is_url)
1854 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1855 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1856 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1857 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1859 else
1861 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1862 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1863 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1864 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1866 if (buffer[end_offset] != '\0')
1868 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1869 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1870 if (buffer[end_offset +1] != '\0')
1872 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1873 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1877 DestroyWindow(hwndRichEdit);
1878 hwndRichEdit = NULL;
1881 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1882 /* Test just the first two URL examples for brevity */
1883 for (i = 0; i < 2; i++) {
1884 SETTEXTEX st;
1886 hwndRichEdit = new_richedit(parent);
1888 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1889 be detected:
1890 1) Set entire text, a la WM_SETTEXT
1891 2) Set a selection of the text to the URL
1892 3) Set a portion of the text at a time, which eventually results in
1893 an URL
1894 All of them should give equivalent results
1897 /* Set entire text in one go, like WM_SETTEXT */
1898 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1899 char * at_pos;
1900 int at_offset;
1901 int end_offset;
1903 st.codepage = CP_ACP;
1904 st.flags = ST_DEFAULT;
1906 at_pos = strchr(templates_delim[j], 'X');
1907 at_offset = at_pos - templates_delim[j];
1908 strncpy(buffer, templates_delim[j], at_offset);
1909 buffer[at_offset] = '\0';
1910 strcat(buffer, urls[i].text);
1911 strcat(buffer, templates_delim[j] + at_offset + 1);
1912 end_offset = at_offset + strlen(urls[i].text);
1914 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1915 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1917 /* This assumes no templates start with the URL itself, and that they
1918 have at least two characters before the URL text */
1919 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1920 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1921 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1922 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1923 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1924 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1926 if (urls[i].is_url)
1928 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1929 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1930 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1931 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1933 else
1935 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1936 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1937 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1938 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1940 if (buffer[end_offset] != '\0')
1942 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1943 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1944 if (buffer[end_offset +1] != '\0')
1946 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1947 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1952 /* Set selection with X to the URL */
1953 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1954 char * at_pos;
1955 int at_offset;
1956 int end_offset;
1958 at_pos = strchr(templates_delim[j], 'X');
1959 at_offset = at_pos - templates_delim[j];
1960 end_offset = at_offset + strlen(urls[i].text);
1962 st.codepage = CP_ACP;
1963 st.flags = ST_DEFAULT;
1964 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1965 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1966 st.flags = ST_SELECTION;
1967 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1968 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1969 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1971 /* This assumes no templates start with the URL itself, and that they
1972 have at least two characters before the URL text */
1973 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1974 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1975 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1976 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1977 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1978 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1980 if (urls[i].is_url)
1982 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1983 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1984 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1985 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1987 else
1989 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1990 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1991 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1992 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1994 if (buffer[end_offset] != '\0')
1996 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1997 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1998 if (buffer[end_offset +1] != '\0')
2000 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2001 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2006 /* Set selection with X to the first character of the URL, then the rest */
2007 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2008 char * at_pos;
2009 int at_offset;
2010 int end_offset;
2012 at_pos = strchr(templates_delim[j], 'X');
2013 at_offset = at_pos - templates_delim[j];
2014 end_offset = at_offset + strlen(urls[i].text);
2016 strcpy(buffer, "YY");
2017 buffer[0] = urls[i].text[0];
2019 st.codepage = CP_ACP;
2020 st.flags = ST_DEFAULT;
2021 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2022 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2023 st.flags = ST_SELECTION;
2024 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2025 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2026 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2027 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2028 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2030 /* This assumes no templates start with the URL itself, and that they
2031 have at least two characters before the URL text */
2032 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2033 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2034 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2035 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2036 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2037 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2039 if (urls[i].is_url)
2041 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2042 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2043 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2044 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2046 else
2048 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2049 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2050 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2051 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2053 if (buffer[end_offset] != '\0')
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2057 if (buffer[end_offset +1] != '\0')
2059 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2060 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2065 DestroyWindow(hwndRichEdit);
2066 hwndRichEdit = NULL;
2069 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2070 /* Test just the first two URL examples for brevity */
2071 for (i = 0; i < 2; i++) {
2072 hwndRichEdit = new_richedit(parent);
2074 /* Set selection with X to the URL */
2075 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2076 char * at_pos;
2077 int at_offset;
2078 int end_offset;
2080 at_pos = strchr(templates_delim[j], 'X');
2081 at_offset = at_pos - templates_delim[j];
2082 end_offset = at_offset + strlen(urls[i].text);
2084 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2085 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2086 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2087 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2088 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2090 /* This assumes no templates start with the URL itself, and that they
2091 have at least two characters before the URL text */
2092 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2093 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2094 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2095 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2096 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2097 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2099 if (urls[i].is_url)
2101 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2102 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2103 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2104 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2106 else
2108 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2109 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2110 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2111 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2113 if (buffer[end_offset] != '\0')
2115 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2116 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2117 if (buffer[end_offset +1] != '\0')
2119 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2120 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2125 /* Set selection with X to the first character of the URL, then the rest */
2126 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2127 char * at_pos;
2128 int at_offset;
2129 int end_offset;
2131 at_pos = strchr(templates_delim[j], 'X');
2132 at_offset = at_pos - templates_delim[j];
2133 end_offset = at_offset + strlen(urls[i].text);
2135 strcpy(buffer, "YY");
2136 buffer[0] = urls[i].text[0];
2138 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2139 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2140 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2141 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2142 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2143 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2144 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2146 /* This assumes no templates start with the URL itself, and that they
2147 have at least two characters before the URL text */
2148 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2149 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2150 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2151 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2152 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2153 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2155 if (urls[i].is_url)
2157 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2158 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2159 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2160 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2162 else
2164 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2165 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2166 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2167 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2169 if (buffer[end_offset] != '\0')
2171 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2172 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2173 if (buffer[end_offset +1] != '\0')
2175 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2176 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2181 DestroyWindow(hwndRichEdit);
2182 hwndRichEdit = NULL;
2185 DestroyWindow(parent);
2188 static void test_EM_SCROLL(void)
2190 int i, j;
2191 int r; /* return value */
2192 int expr; /* expected return value */
2193 HWND hwndRichEdit = new_richedit(NULL);
2194 int y_before, y_after; /* units of lines of text */
2196 /* test a richedit box containing a single line of text */
2197 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2198 expr = 0x00010000;
2199 for (i = 0; i < 4; i++) {
2200 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2202 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2203 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2204 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2205 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2206 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2207 "(i == %d)\n", y_after, i);
2211 * test a richedit box that will scroll. There are two general
2212 * cases: the case without any long lines and the case with a long
2213 * line.
2215 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2216 if (i == 0)
2217 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2218 else
2219 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2220 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2221 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2222 "LONG LINE \nb\nc\nd\ne");
2223 for (j = 0; j < 12; j++) /* reset scroll position to top */
2224 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2226 /* get first visible line */
2227 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2228 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2230 /* get new current first visible line */
2231 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2233 ok(((r & 0xffffff00) == 0x00010000) &&
2234 ((r & 0x000000ff) != 0x00000000),
2235 "EM_SCROLL page down didn't scroll by a small positive number of "
2236 "lines (r == 0x%08x)\n", r);
2237 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2238 "(line %d scrolled to line %d\n", y_before, y_after);
2240 y_before = y_after;
2242 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2243 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2244 ok(((r & 0xffffff00) == 0x0001ff00),
2245 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2246 "(r == 0x%08x)\n", r);
2247 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2248 "%d scrolled to line %d\n", y_before, y_after);
2250 y_before = y_after;
2252 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2254 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2256 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2257 "(r == 0x%08x)\n", r);
2258 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2259 "1 line (%d scrolled to %d)\n", y_before, y_after);
2261 y_before = y_after;
2263 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2265 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2267 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2268 "(r == 0x%08x)\n", r);
2269 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2270 "line (%d scrolled to %d)\n", y_before, y_after);
2272 y_before = y_after;
2274 r = SendMessage(hwndRichEdit, EM_SCROLL,
2275 SB_LINEUP, 0); /* lineup beyond top */
2277 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2279 ok(r == 0x00010000,
2280 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2281 ok(y_before == y_after,
2282 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2284 y_before = y_after;
2286 r = SendMessage(hwndRichEdit, EM_SCROLL,
2287 SB_PAGEUP, 0);/*page up beyond top */
2289 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2291 ok(r == 0x00010000,
2292 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2293 ok(y_before == y_after,
2294 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2296 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2297 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2298 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2299 r = SendMessage(hwndRichEdit, EM_SCROLL,
2300 SB_PAGEDOWN, 0); /* page down beyond bot */
2301 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2303 ok(r == 0x00010000,
2304 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2305 ok(y_before == y_after,
2306 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2307 y_before, y_after);
2309 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2310 SendMessage(hwndRichEdit, EM_SCROLL,
2311 SB_LINEDOWN, 0); /* line down beyond bot */
2312 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2314 ok(r == 0x00010000,
2315 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2316 ok(y_before == y_after,
2317 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2318 y_before, y_after);
2320 DestroyWindow(hwndRichEdit);
2323 unsigned int recursionLevel = 0;
2324 unsigned int WM_SIZE_recursionLevel = 0;
2325 BOOL bailedOutOfRecursion = FALSE;
2326 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2328 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2330 LRESULT r;
2332 if (bailedOutOfRecursion) return 0;
2333 if (recursionLevel >= 32) {
2334 bailedOutOfRecursion = TRUE;
2335 return 0;
2338 recursionLevel++;
2339 switch (message) {
2340 case WM_SIZE:
2341 WM_SIZE_recursionLevel++;
2342 r = richeditProc(hwnd, message, wParam, lParam);
2343 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2344 ShowScrollBar(hwnd, SB_VERT, TRUE);
2345 WM_SIZE_recursionLevel--;
2346 break;
2347 default:
2348 r = richeditProc(hwnd, message, wParam, lParam);
2349 break;
2351 recursionLevel--;
2352 return r;
2355 static void test_scrollbar_visibility(void)
2357 HWND hwndRichEdit;
2358 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2359 SCROLLINFO si;
2360 WNDCLASSA cls;
2361 BOOL r;
2363 /* These tests show that richedit should temporarily refrain from automatically
2364 hiding or showing its scrollbars (vertical at least) when an explicit request
2365 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2366 Some applications depend on forced showing (when otherwise richedit would
2367 hide the vertical scrollbar) and are thrown on an endless recursive loop
2368 if richedit auto-hides the scrollbar again. Apparently they never heard of
2369 the ES_DISABLENOSCROLL style... */
2371 hwndRichEdit = new_richedit(NULL);
2373 /* Test default scrollbar visibility behavior */
2374 memset(&si, 0, sizeof(si));
2375 si.cbSize = sizeof(si);
2376 si.fMask = SIF_PAGE | SIF_RANGE;
2377 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2378 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2379 "Vertical scrollbar is visible, should be invisible.\n");
2380 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2381 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2382 si.nPage, si.nMin, si.nMax);
2384 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2385 memset(&si, 0, sizeof(si));
2386 si.cbSize = sizeof(si);
2387 si.fMask = SIF_PAGE | SIF_RANGE;
2388 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2389 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2390 "Vertical scrollbar is visible, should be invisible.\n");
2391 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2392 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2393 si.nPage, si.nMin, si.nMax);
2395 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2396 memset(&si, 0, sizeof(si));
2397 si.cbSize = sizeof(si);
2398 si.fMask = SIF_PAGE | SIF_RANGE;
2399 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2400 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2401 "Vertical scrollbar is invisible, should be visible.\n");
2402 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2403 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2404 si.nPage, si.nMin, si.nMax);
2406 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2407 even though it hides the scrollbar */
2408 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2409 memset(&si, 0, sizeof(si));
2410 si.cbSize = sizeof(si);
2411 si.fMask = SIF_PAGE | SIF_RANGE;
2412 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2413 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2414 "Vertical scrollbar is visible, should be invisible.\n");
2415 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2416 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2417 si.nPage, si.nMin, si.nMax);
2419 /* Setting non-scrolling text again does *not* reset scrollbar range */
2420 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2421 memset(&si, 0, sizeof(si));
2422 si.cbSize = sizeof(si);
2423 si.fMask = SIF_PAGE | SIF_RANGE;
2424 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2425 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2426 "Vertical scrollbar is visible, should be invisible.\n");
2427 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2428 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2429 si.nPage, si.nMin, si.nMax);
2431 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2432 memset(&si, 0, sizeof(si));
2433 si.cbSize = sizeof(si);
2434 si.fMask = SIF_PAGE | SIF_RANGE;
2435 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2436 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2437 "Vertical scrollbar is visible, should be invisible.\n");
2438 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2439 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2440 si.nPage, si.nMin, si.nMax);
2442 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2443 memset(&si, 0, sizeof(si));
2444 si.cbSize = sizeof(si);
2445 si.fMask = SIF_PAGE | SIF_RANGE;
2446 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2447 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2448 "Vertical scrollbar is visible, should be invisible.\n");
2449 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2450 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2451 si.nPage, si.nMin, si.nMax);
2453 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2454 memset(&si, 0, sizeof(si));
2455 si.cbSize = sizeof(si);
2456 si.fMask = SIF_PAGE | SIF_RANGE;
2457 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2458 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2459 "Vertical scrollbar is visible, should be invisible.\n");
2460 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2461 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2462 si.nPage, si.nMin, si.nMax);
2464 DestroyWindow(hwndRichEdit);
2466 /* Test again, with ES_DISABLENOSCROLL style */
2467 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2469 /* Test default scrollbar visibility behavior */
2470 memset(&si, 0, sizeof(si));
2471 si.cbSize = sizeof(si);
2472 si.fMask = SIF_PAGE | SIF_RANGE;
2473 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2474 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2475 "Vertical scrollbar is invisible, should be visible.\n");
2476 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2477 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2478 si.nPage, si.nMin, si.nMax);
2480 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2481 memset(&si, 0, sizeof(si));
2482 si.cbSize = sizeof(si);
2483 si.fMask = SIF_PAGE | SIF_RANGE;
2484 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2485 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2486 "Vertical scrollbar is invisible, should be visible.\n");
2487 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2488 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2489 si.nPage, si.nMin, si.nMax);
2491 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2492 memset(&si, 0, sizeof(si));
2493 si.cbSize = sizeof(si);
2494 si.fMask = SIF_PAGE | SIF_RANGE;
2495 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2496 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2497 "Vertical scrollbar is invisible, should be visible.\n");
2498 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2499 "reported page/range is %d (%d..%d)\n",
2500 si.nPage, si.nMin, si.nMax);
2502 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2503 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2504 memset(&si, 0, sizeof(si));
2505 si.cbSize = sizeof(si);
2506 si.fMask = SIF_PAGE | SIF_RANGE;
2507 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2508 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2509 "Vertical scrollbar is invisible, should be visible.\n");
2510 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2511 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2512 si.nPage, si.nMin, si.nMax);
2514 /* Setting non-scrolling text again does *not* reset scrollbar range */
2515 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2516 memset(&si, 0, sizeof(si));
2517 si.cbSize = sizeof(si);
2518 si.fMask = SIF_PAGE | SIF_RANGE;
2519 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2520 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2521 "Vertical scrollbar is invisible, should be visible.\n");
2522 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2523 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2524 si.nPage, si.nMin, si.nMax);
2526 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2527 memset(&si, 0, sizeof(si));
2528 si.cbSize = sizeof(si);
2529 si.fMask = SIF_PAGE | SIF_RANGE;
2530 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2531 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2532 "Vertical scrollbar is invisible, should be visible.\n");
2533 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2534 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2535 si.nPage, si.nMin, si.nMax);
2537 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2538 memset(&si, 0, sizeof(si));
2539 si.cbSize = sizeof(si);
2540 si.fMask = SIF_PAGE | SIF_RANGE;
2541 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2542 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2543 "Vertical scrollbar is invisible, should be visible.\n");
2544 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2545 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2546 si.nPage, si.nMin, si.nMax);
2548 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2549 memset(&si, 0, sizeof(si));
2550 si.cbSize = sizeof(si);
2551 si.fMask = SIF_PAGE | SIF_RANGE;
2552 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2553 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2554 "Vertical scrollbar is invisible, should be visible.\n");
2555 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2556 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2557 si.nPage, si.nMin, si.nMax);
2559 DestroyWindow(hwndRichEdit);
2561 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2562 hwndRichEdit = new_richedit(NULL);
2564 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2565 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2566 memset(&si, 0, sizeof(si));
2567 si.cbSize = sizeof(si);
2568 si.fMask = SIF_PAGE | SIF_RANGE;
2569 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2570 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2571 "Vertical scrollbar is invisible, should be visible.\n");
2572 todo_wine {
2573 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2574 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2575 si.nPage, si.nMin, si.nMax);
2578 /* Ditto, see above */
2579 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2580 memset(&si, 0, sizeof(si));
2581 si.cbSize = sizeof(si);
2582 si.fMask = SIF_PAGE | SIF_RANGE;
2583 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2584 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2585 "Vertical scrollbar is invisible, should be visible.\n");
2586 todo_wine {
2587 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2588 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2589 si.nPage, si.nMin, si.nMax);
2592 /* Ditto, see above */
2593 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2594 memset(&si, 0, sizeof(si));
2595 si.cbSize = sizeof(si);
2596 si.fMask = SIF_PAGE | SIF_RANGE;
2597 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2598 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2599 "Vertical scrollbar is invisible, should be visible.\n");
2600 todo_wine {
2601 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2602 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2603 si.nPage, si.nMin, si.nMax);
2606 /* Ditto, see above */
2607 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2608 memset(&si, 0, sizeof(si));
2609 si.cbSize = sizeof(si);
2610 si.fMask = SIF_PAGE | SIF_RANGE;
2611 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2612 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2613 "Vertical scrollbar is invisible, should be visible.\n");
2614 todo_wine {
2615 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2616 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2617 si.nPage, si.nMin, si.nMax);
2620 /* Ditto, see above */
2621 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2622 memset(&si, 0, sizeof(si));
2623 si.cbSize = sizeof(si);
2624 si.fMask = SIF_PAGE | SIF_RANGE;
2625 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2626 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2627 "Vertical scrollbar is invisible, should be visible.\n");
2628 todo_wine {
2629 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2630 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2631 si.nPage, si.nMin, si.nMax);
2634 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2635 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2636 memset(&si, 0, sizeof(si));
2637 si.cbSize = sizeof(si);
2638 si.fMask = SIF_PAGE | SIF_RANGE;
2639 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2640 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2641 "Vertical scrollbar is visible, should be invisible.\n");
2642 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2643 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2644 si.nPage, si.nMin, si.nMax);
2646 DestroyWindow(hwndRichEdit);
2648 hwndRichEdit = new_richedit(NULL);
2650 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2651 memset(&si, 0, sizeof(si));
2652 si.cbSize = sizeof(si);
2653 si.fMask = SIF_PAGE | SIF_RANGE;
2654 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2655 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2656 "Vertical scrollbar is visible, should be invisible.\n");
2657 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2658 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2659 si.nPage, si.nMin, si.nMax);
2661 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2662 memset(&si, 0, sizeof(si));
2663 si.cbSize = sizeof(si);
2664 si.fMask = SIF_PAGE | SIF_RANGE;
2665 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2666 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2667 "Vertical scrollbar is visible, should be invisible.\n");
2668 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2669 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2670 si.nPage, si.nMin, si.nMax);
2672 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2673 memset(&si, 0, sizeof(si));
2674 si.cbSize = sizeof(si);
2675 si.fMask = SIF_PAGE | SIF_RANGE;
2676 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2677 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2678 "Vertical scrollbar is visible, should be invisible.\n");
2679 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2680 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2681 si.nPage, si.nMin, si.nMax);
2683 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2684 memset(&si, 0, sizeof(si));
2685 si.cbSize = sizeof(si);
2686 si.fMask = SIF_PAGE | SIF_RANGE;
2687 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2688 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2689 "Vertical scrollbar is visible, should be invisible.\n");
2690 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2691 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2692 si.nPage, si.nMin, si.nMax);
2694 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2695 memset(&si, 0, sizeof(si));
2696 si.cbSize = sizeof(si);
2697 si.fMask = SIF_PAGE | SIF_RANGE;
2698 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2699 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2700 "Vertical scrollbar is invisible, should be visible.\n");
2701 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2702 "reported page/range is %d (%d..%d)\n",
2703 si.nPage, si.nMin, si.nMax);
2705 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2706 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2707 memset(&si, 0, sizeof(si));
2708 si.cbSize = sizeof(si);
2709 si.fMask = SIF_PAGE | SIF_RANGE;
2710 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2711 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2712 "Vertical scrollbar is visible, should be invisible.\n");
2713 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2714 "reported page/range is %d (%d..%d)\n",
2715 si.nPage, si.nMin, si.nMax);
2717 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2718 memset(&si, 0, sizeof(si));
2719 si.cbSize = sizeof(si);
2720 si.fMask = SIF_PAGE | SIF_RANGE;
2721 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2722 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2723 "Vertical scrollbar is visible, should be invisible.\n");
2724 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2725 "reported page/range is %d (%d..%d)\n",
2726 si.nPage, si.nMin, si.nMax);
2728 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2729 EM_SCROLL will make visible any forcefully invisible scrollbar */
2730 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2731 memset(&si, 0, sizeof(si));
2732 si.cbSize = sizeof(si);
2733 si.fMask = SIF_PAGE | SIF_RANGE;
2734 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2735 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2736 "Vertical scrollbar is invisible, should be visible.\n");
2737 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2738 "reported page/range is %d (%d..%d)\n",
2739 si.nPage, si.nMin, si.nMax);
2741 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2742 memset(&si, 0, sizeof(si));
2743 si.cbSize = sizeof(si);
2744 si.fMask = SIF_PAGE | SIF_RANGE;
2745 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2746 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2747 "Vertical scrollbar is visible, should be invisible.\n");
2748 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2749 "reported page/range is %d (%d..%d)\n",
2750 si.nPage, si.nMin, si.nMax);
2752 /* Again, EM_SCROLL, with SB_LINEUP */
2753 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2754 memset(&si, 0, sizeof(si));
2755 si.cbSize = sizeof(si);
2756 si.fMask = SIF_PAGE | SIF_RANGE;
2757 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2758 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2759 "Vertical scrollbar is invisible, should be visible.\n");
2760 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2761 "reported page/range is %d (%d..%d)\n",
2762 si.nPage, si.nMin, si.nMax);
2764 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2765 memset(&si, 0, sizeof(si));
2766 si.cbSize = sizeof(si);
2767 si.fMask = SIF_PAGE | SIF_RANGE;
2768 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2769 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2770 "Vertical scrollbar is visible, should be invisible.\n");
2771 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2772 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2773 si.nPage, si.nMin, si.nMax);
2775 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2776 memset(&si, 0, sizeof(si));
2777 si.cbSize = sizeof(si);
2778 si.fMask = SIF_PAGE | SIF_RANGE;
2779 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2780 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2781 "Vertical scrollbar is invisible, should be visible.\n");
2782 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2783 "reported page/range is %d (%d..%d)\n",
2784 si.nPage, si.nMin, si.nMax);
2786 DestroyWindow(hwndRichEdit);
2789 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2790 hwndRichEdit = new_richedit(NULL);
2792 #define ENABLE_WS_VSCROLL(hwnd) \
2793 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2794 #define DISABLE_WS_VSCROLL(hwnd) \
2795 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2797 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2798 ENABLE_WS_VSCROLL(hwndRichEdit);
2799 memset(&si, 0, sizeof(si));
2800 si.cbSize = sizeof(si);
2801 si.fMask = SIF_PAGE | SIF_RANGE;
2802 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2803 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2804 "Vertical scrollbar is invisible, should be visible.\n");
2805 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2806 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2807 si.nPage, si.nMin, si.nMax);
2809 /* Ditto, see above */
2810 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2811 memset(&si, 0, sizeof(si));
2812 si.cbSize = sizeof(si);
2813 si.fMask = SIF_PAGE | SIF_RANGE;
2814 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2815 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2816 "Vertical scrollbar is invisible, should be visible.\n");
2817 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2818 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2819 si.nPage, si.nMin, si.nMax);
2821 /* Ditto, see above */
2822 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2823 memset(&si, 0, sizeof(si));
2824 si.cbSize = sizeof(si);
2825 si.fMask = SIF_PAGE | SIF_RANGE;
2826 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2827 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2828 "Vertical scrollbar is invisible, should be visible.\n");
2829 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2830 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2831 si.nPage, si.nMin, si.nMax);
2833 /* Ditto, see above */
2834 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2835 memset(&si, 0, sizeof(si));
2836 si.cbSize = sizeof(si);
2837 si.fMask = SIF_PAGE | SIF_RANGE;
2838 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2839 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2840 "Vertical scrollbar is invisible, should be visible.\n");
2841 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2842 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2843 si.nPage, si.nMin, si.nMax);
2845 /* Ditto, see above */
2846 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2847 memset(&si, 0, sizeof(si));
2848 si.cbSize = sizeof(si);
2849 si.fMask = SIF_PAGE | SIF_RANGE;
2850 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2851 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2852 "Vertical scrollbar is invisible, should be visible.\n");
2853 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2854 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2855 si.nPage, si.nMin, si.nMax);
2857 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2858 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2859 memset(&si, 0, sizeof(si));
2860 si.cbSize = sizeof(si);
2861 si.fMask = SIF_PAGE | SIF_RANGE;
2862 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2863 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2864 "Vertical scrollbar is visible, should be invisible.\n");
2865 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2866 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2867 si.nPage, si.nMin, si.nMax);
2869 DestroyWindow(hwndRichEdit);
2871 hwndRichEdit = new_richedit(NULL);
2873 DISABLE_WS_VSCROLL(hwndRichEdit);
2874 memset(&si, 0, sizeof(si));
2875 si.cbSize = sizeof(si);
2876 si.fMask = SIF_PAGE | SIF_RANGE;
2877 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2878 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2879 "Vertical scrollbar is visible, should be invisible.\n");
2880 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2881 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2882 si.nPage, si.nMin, si.nMax);
2884 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2885 memset(&si, 0, sizeof(si));
2886 si.cbSize = sizeof(si);
2887 si.fMask = SIF_PAGE | SIF_RANGE;
2888 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2889 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2890 "Vertical scrollbar is visible, should be invisible.\n");
2891 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2892 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2893 si.nPage, si.nMin, si.nMax);
2895 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2896 memset(&si, 0, sizeof(si));
2897 si.cbSize = sizeof(si);
2898 si.fMask = SIF_PAGE | SIF_RANGE;
2899 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2900 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2901 "Vertical scrollbar is visible, should be invisible.\n");
2902 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2903 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2904 si.nPage, si.nMin, si.nMax);
2906 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2907 memset(&si, 0, sizeof(si));
2908 si.cbSize = sizeof(si);
2909 si.fMask = SIF_PAGE | SIF_RANGE;
2910 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2911 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2912 "Vertical scrollbar is visible, should be invisible.\n");
2913 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2914 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2915 si.nPage, si.nMin, si.nMax);
2917 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2918 memset(&si, 0, sizeof(si));
2919 si.cbSize = sizeof(si);
2920 si.fMask = SIF_PAGE | SIF_RANGE;
2921 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2922 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2923 "Vertical scrollbar is invisible, should be visible.\n");
2924 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2925 "reported page/range is %d (%d..%d)\n",
2926 si.nPage, si.nMin, si.nMax);
2928 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2929 DISABLE_WS_VSCROLL(hwndRichEdit);
2930 memset(&si, 0, sizeof(si));
2931 si.cbSize = sizeof(si);
2932 si.fMask = SIF_PAGE | SIF_RANGE;
2933 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2934 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2935 "Vertical scrollbar is visible, should be invisible.\n");
2936 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2937 "reported page/range is %d (%d..%d)\n",
2938 si.nPage, si.nMin, si.nMax);
2940 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2941 memset(&si, 0, sizeof(si));
2942 si.cbSize = sizeof(si);
2943 si.fMask = SIF_PAGE | SIF_RANGE;
2944 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2945 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2946 "Vertical scrollbar is visible, should be invisible.\n");
2947 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2948 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2949 si.nPage, si.nMin, si.nMax);
2951 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2952 memset(&si, 0, sizeof(si));
2953 si.cbSize = sizeof(si);
2954 si.fMask = SIF_PAGE | SIF_RANGE;
2955 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2956 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2957 "Vertical scrollbar is invisible, should be visible.\n");
2958 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2959 "reported page/range is %d (%d..%d)\n",
2960 si.nPage, si.nMin, si.nMax);
2962 DISABLE_WS_VSCROLL(hwndRichEdit);
2963 memset(&si, 0, sizeof(si));
2964 si.cbSize = sizeof(si);
2965 si.fMask = SIF_PAGE | SIF_RANGE;
2966 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2967 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2968 "Vertical scrollbar is visible, should be invisible.\n");
2969 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2970 "reported page/range is %d (%d..%d)\n",
2971 si.nPage, si.nMin, si.nMax);
2973 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2974 EM_SCROLL will make visible any forcefully invisible scrollbar */
2975 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2976 memset(&si, 0, sizeof(si));
2977 si.cbSize = sizeof(si);
2978 si.fMask = SIF_PAGE | SIF_RANGE;
2979 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2980 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2981 "Vertical scrollbar is invisible, should be visible.\n");
2982 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2983 "reported page/range is %d (%d..%d)\n",
2984 si.nPage, si.nMin, si.nMax);
2986 DISABLE_WS_VSCROLL(hwndRichEdit);
2987 memset(&si, 0, sizeof(si));
2988 si.cbSize = sizeof(si);
2989 si.fMask = SIF_PAGE | SIF_RANGE;
2990 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2991 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2992 "Vertical scrollbar is visible, should be invisible.\n");
2993 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2994 "reported page/range is %d (%d..%d)\n",
2995 si.nPage, si.nMin, si.nMax);
2997 /* Again, EM_SCROLL, with SB_LINEUP */
2998 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2999 memset(&si, 0, sizeof(si));
3000 si.cbSize = sizeof(si);
3001 si.fMask = SIF_PAGE | SIF_RANGE;
3002 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3003 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3004 "Vertical scrollbar is invisible, should be visible.\n");
3005 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3006 "reported page/range is %d (%d..%d)\n",
3007 si.nPage, si.nMin, si.nMax);
3009 DestroyWindow(hwndRichEdit);
3011 /* This window proc models what is going on with Corman Lisp 3.0.
3012 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3013 force the scrollbar into visibility. Recursion should NOT happen
3014 as a result of this action.
3016 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3017 if (r) {
3018 richeditProc = cls.lpfnWndProc;
3019 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3020 cls.lpszClassName = "RicheditStupidOverride";
3021 if(!RegisterClassA(&cls)) assert(0);
3023 recursionLevel = 0;
3024 WM_SIZE_recursionLevel = 0;
3025 bailedOutOfRecursion = FALSE;
3026 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3027 ok(!bailedOutOfRecursion,
3028 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3030 recursionLevel = 0;
3031 WM_SIZE_recursionLevel = 0;
3032 bailedOutOfRecursion = FALSE;
3033 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3034 ok(!bailedOutOfRecursion,
3035 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3037 /* Unblock window in order to process WM_DESTROY */
3038 recursionLevel = 0;
3039 bailedOutOfRecursion = FALSE;
3040 WM_SIZE_recursionLevel = 0;
3041 DestroyWindow(hwndRichEdit);
3045 static void test_EM_SETUNDOLIMIT(void)
3047 /* cases we test for:
3048 * default behaviour - limiting at 100 undo's
3049 * undo disabled - setting a limit of 0
3050 * undo limited - undo limit set to some to some number, like 2
3051 * bad input - sending a negative number should default to 100 undo's */
3053 HWND hwndRichEdit = new_richedit(NULL);
3054 CHARRANGE cr;
3055 int i;
3056 int result;
3058 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3059 cr.cpMin = 0;
3060 cr.cpMax = 1;
3061 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3062 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3063 also, multiple pastes don't combine like WM_CHAR would */
3064 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3066 /* first case - check the default */
3067 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3068 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3069 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3070 for (i=0; i<100; i++) /* Undo 100 of them */
3071 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3072 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3073 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3075 /* second case - cannot undo */
3076 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3077 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3078 SendMessage(hwndRichEdit,
3079 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3080 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3081 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3083 /* third case - set it to an arbitrary number */
3084 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3085 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3086 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3087 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3088 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3089 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3090 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3091 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3092 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3093 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3094 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3095 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3096 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3097 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3099 /* fourth case - setting negative numbers should default to 100 undos */
3100 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3101 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3102 ok (result == 100,
3103 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3105 DestroyWindow(hwndRichEdit);
3108 static void test_ES_PASSWORD(void)
3110 /* This isn't hugely testable, so we're just going to run it through its paces */
3112 HWND hwndRichEdit = new_richedit(NULL);
3113 WCHAR result;
3115 /* First, check the default of a regular control */
3116 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3117 ok (result == 0,
3118 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3120 /* Now, set it to something normal */
3121 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3122 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3123 ok (result == 120,
3124 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3126 /* Now, set it to something odd */
3127 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3128 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3129 ok (result == 1234,
3130 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3131 DestroyWindow(hwndRichEdit);
3134 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3135 LPBYTE pbBuff,
3136 LONG cb,
3137 LONG *pcb)
3139 char** str = (char**)dwCookie;
3140 *pcb = cb;
3141 if (*pcb > 0) {
3142 memcpy(*str, pbBuff, *pcb);
3143 *str += *pcb;
3145 return 0;
3148 static void test_WM_SETTEXT()
3150 HWND hwndRichEdit = new_richedit(NULL);
3151 const char * TestItem1 = "TestSomeText";
3152 const char * TestItem2 = "TestSomeText\r";
3153 const char * TestItem2_after = "TestSomeText\r\n";
3154 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3155 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3156 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3157 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3158 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3159 const char * TestItem5_after = "TestSomeText TestSomeText";
3160 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3161 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3162 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3163 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3165 char buf[1024] = {0};
3166 LRESULT result;
3167 EDITSTREAM es;
3168 char * p;
3170 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3171 any solitary \r to be converted to \r\n on return. Properly paired
3172 \r\n are not affected. It also shows that the special sequence \r\r\n
3173 gets converted to a single space.
3176 #define TEST_SETTEXT(a, b) \
3177 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3178 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3179 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3180 ok (result == lstrlen(buf), \
3181 "WM_GETTEXT returned %ld instead of expected %u\n", \
3182 result, lstrlen(buf)); \
3183 result = strcmp(b, buf); \
3184 ok(result == 0, \
3185 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3187 TEST_SETTEXT(TestItem1, TestItem1)
3188 TEST_SETTEXT(TestItem2, TestItem2_after)
3189 TEST_SETTEXT(TestItem3, TestItem3_after)
3190 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3191 TEST_SETTEXT(TestItem4, TestItem4_after)
3192 TEST_SETTEXT(TestItem5, TestItem5_after)
3193 TEST_SETTEXT(TestItem6, TestItem6_after)
3194 TEST_SETTEXT(TestItem7, TestItem7_after)
3196 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3197 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3198 p = buf;
3199 es.dwCookie = (DWORD_PTR)&p;
3200 es.dwError = 0;
3201 es.pfnCallback = test_WM_SETTEXT_esCallback;
3202 memset(buf, 0, sizeof(buf));
3203 SendMessage(hwndRichEdit, EM_STREAMOUT,
3204 (WPARAM)(SF_RTF), (LPARAM)&es);
3205 trace("EM_STREAMOUT produced: \n%s\n", buf);
3206 TEST_SETTEXT(buf, TestItem1)
3208 #undef TEST_SETTEXT
3209 DestroyWindow(hwndRichEdit);
3212 static void test_EM_STREAMOUT(void)
3214 HWND hwndRichEdit = new_richedit(NULL);
3215 int r;
3216 EDITSTREAM es;
3217 char buf[1024] = {0};
3218 char * p;
3220 const char * TestItem1 = "TestSomeText";
3221 const char * TestItem2 = "TestSomeText\r";
3222 const char * TestItem3 = "TestSomeText\r\n";
3224 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3225 p = buf;
3226 es.dwCookie = (DWORD_PTR)&p;
3227 es.dwError = 0;
3228 es.pfnCallback = test_WM_SETTEXT_esCallback;
3229 memset(buf, 0, sizeof(buf));
3230 SendMessage(hwndRichEdit, EM_STREAMOUT,
3231 (WPARAM)(SF_TEXT), (LPARAM)&es);
3232 r = strlen(buf);
3233 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3234 ok(strcmp(buf, TestItem1) == 0,
3235 "streamed text different, got %s\n", buf);
3237 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3238 p = buf;
3239 es.dwCookie = (DWORD_PTR)&p;
3240 es.dwError = 0;
3241 es.pfnCallback = test_WM_SETTEXT_esCallback;
3242 memset(buf, 0, sizeof(buf));
3243 SendMessage(hwndRichEdit, EM_STREAMOUT,
3244 (WPARAM)(SF_TEXT), (LPARAM)&es);
3245 r = strlen(buf);
3246 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3247 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3248 ok(strcmp(buf, TestItem3) == 0,
3249 "streamed text different from, got %s\n", buf);
3250 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3251 p = buf;
3252 es.dwCookie = (DWORD_PTR)&p;
3253 es.dwError = 0;
3254 es.pfnCallback = test_WM_SETTEXT_esCallback;
3255 memset(buf, 0, sizeof(buf));
3256 SendMessage(hwndRichEdit, EM_STREAMOUT,
3257 (WPARAM)(SF_TEXT), (LPARAM)&es);
3258 r = strlen(buf);
3259 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3260 ok(strcmp(buf, TestItem3) == 0,
3261 "streamed text different, got %s\n", buf);
3263 DestroyWindow(hwndRichEdit);
3266 static void test_EM_SETTEXTEX(void)
3268 HWND hwndRichEdit = new_richedit(NULL);
3269 SETTEXTEX setText;
3270 GETTEXTEX getText;
3271 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3272 'S', 'o', 'm', 'e',
3273 'T', 'e', 'x', 't', 0};
3274 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3275 't', 'S', 'o', 'm',
3276 'e', 'T', 'e', 'x',
3277 't', 't', 'S', 'o',
3278 'm', 'e', 'T', 'e',
3279 'x', 't', 0};
3280 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3281 '\r','t','S','o','m','e','T','e','x','t',0};
3282 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3283 'S', 'o', 'm', 'e',
3284 'T', 'e', 'x', 't',
3285 '\r', 0};
3286 const char * TestItem2_after = "TestSomeText\r\n";
3287 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3288 'S', 'o', 'm', 'e',
3289 'T', 'e', 'x', 't',
3290 '\r','\n','\r','\n', 0};
3291 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3292 'S', 'o', 'm', 'e',
3293 'T', 'e', 'x', 't',
3294 '\n','\n', 0};
3295 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3296 'S', 'o', 'm', 'e',
3297 'T', 'e', 'x', 't',
3298 '\r','\r', 0};
3299 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3300 'S', 'o', 'm', 'e',
3301 'T', 'e', 'x', 't',
3302 '\r','\r','\n','\r',
3303 '\n', 0};
3304 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3305 'S', 'o', 'm', 'e',
3306 'T', 'e', 'x', 't',
3307 ' ','\r', 0};
3308 #define MAX_BUF_LEN 1024
3309 WCHAR buf[MAX_BUF_LEN];
3310 char bufACP[MAX_BUF_LEN];
3311 char * p;
3312 int result;
3313 CHARRANGE cr;
3314 EDITSTREAM es;
3316 setText.codepage = 1200; /* no constant for unicode */
3317 getText.codepage = 1200; /* no constant for unicode */
3318 getText.cb = MAX_BUF_LEN;
3319 getText.flags = GT_DEFAULT;
3320 getText.lpDefaultChar = NULL;
3321 getText.lpUsedDefChar = NULL;
3323 setText.flags = 0;
3324 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3325 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3326 ok(lstrcmpW(buf, TestItem1) == 0,
3327 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3329 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3330 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3332 setText.codepage = 1200; /* no constant for unicode */
3333 getText.codepage = 1200; /* no constant for unicode */
3334 getText.cb = MAX_BUF_LEN;
3335 getText.flags = GT_DEFAULT;
3336 getText.lpDefaultChar = NULL;
3337 getText.lpUsedDefChar = NULL;
3338 setText.flags = 0;
3339 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3340 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3341 ok(lstrcmpW(buf, TestItem2) == 0,
3342 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3344 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3345 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3346 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3347 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3349 /* Baseline test for just-enough buffer space for string */
3350 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3351 getText.codepage = 1200; /* no constant for unicode */
3352 getText.flags = GT_DEFAULT;
3353 getText.lpDefaultChar = NULL;
3354 getText.lpUsedDefChar = NULL;
3355 memset(buf, 0, MAX_BUF_LEN);
3356 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3357 ok(lstrcmpW(buf, TestItem2) == 0,
3358 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3360 /* When there is enough space for one character, but not both, of the CRLF
3361 pair at the end of the string, the CR is not copied at all. That is,
3362 the caller must not see CRLF pairs truncated to CR at the end of the
3363 string.
3365 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3366 getText.codepage = 1200; /* no constant for unicode */
3367 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3368 getText.lpDefaultChar = NULL;
3369 getText.lpUsedDefChar = NULL;
3370 memset(buf, 0, MAX_BUF_LEN);
3371 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3372 ok(lstrcmpW(buf, TestItem1) == 0,
3373 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3376 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3377 setText.codepage = 1200; /* no constant for unicode */
3378 getText.codepage = 1200; /* no constant for unicode */
3379 getText.cb = MAX_BUF_LEN;
3380 getText.flags = GT_DEFAULT;
3381 getText.lpDefaultChar = NULL;
3382 getText.lpUsedDefChar = NULL;
3383 setText.flags = 0;
3384 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3385 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3386 ok(lstrcmpW(buf, TestItem3_after) == 0,
3387 "EM_SETTEXTEX did not convert properly\n");
3389 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3390 setText.codepage = 1200; /* no constant for unicode */
3391 getText.codepage = 1200; /* no constant for unicode */
3392 getText.cb = MAX_BUF_LEN;
3393 getText.flags = GT_DEFAULT;
3394 getText.lpDefaultChar = NULL;
3395 getText.lpUsedDefChar = NULL;
3396 setText.flags = 0;
3397 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3398 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3399 ok(lstrcmpW(buf, TestItem3_after) == 0,
3400 "EM_SETTEXTEX did not convert properly\n");
3402 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3403 setText.codepage = 1200; /* no constant for unicode */
3404 getText.codepage = 1200; /* no constant for unicode */
3405 getText.cb = MAX_BUF_LEN;
3406 getText.flags = GT_DEFAULT;
3407 getText.lpDefaultChar = NULL;
3408 getText.lpUsedDefChar = NULL;
3409 setText.flags = 0;
3410 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3411 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3412 ok(lstrcmpW(buf, TestItem4_after) == 0,
3413 "EM_SETTEXTEX did not convert properly\n");
3415 /* !ST_SELECTION && Unicode && !\rtf */
3416 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3417 (WPARAM)&setText, (LPARAM) NULL);
3418 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3420 ok (result == 1,
3421 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3422 ok(lstrlenW(buf) == 0,
3423 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3425 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3426 setText.flags = 0;
3427 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3428 /* select some text */
3429 cr.cpMax = 1;
3430 cr.cpMin = 3;
3431 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3432 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3433 setText.flags = ST_SELECTION;
3434 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3435 (WPARAM)&setText, (LPARAM) NULL);
3436 ok(result == 0,
3437 "EM_SETTEXTEX with NULL lParam to replace selection"
3438 " with no text should return 0. Got %i\n",
3439 result);
3441 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3442 setText.flags = 0;
3443 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3444 /* select some text */
3445 cr.cpMax = 1;
3446 cr.cpMin = 3;
3447 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3448 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3449 setText.flags = ST_SELECTION;
3450 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3451 (WPARAM)&setText, (LPARAM) TestItem1);
3452 /* get text */
3453 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3454 ok(result == lstrlenW(TestItem1),
3455 "EM_SETTEXTEX with NULL lParam to replace selection"
3456 " with no text should return 0. Got %i\n",
3457 result);
3458 ok(lstrlenW(buf) == 22,
3459 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3460 lstrlenW(buf) );
3462 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3463 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3464 p = (char *)buf;
3465 es.dwCookie = (DWORD_PTR)&p;
3466 es.dwError = 0;
3467 es.pfnCallback = test_WM_SETTEXT_esCallback;
3468 memset(buf, 0, sizeof(buf));
3469 SendMessage(hwndRichEdit, EM_STREAMOUT,
3470 (WPARAM)(SF_RTF), (LPARAM)&es);
3471 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3473 /* !ST_SELECTION && !Unicode && \rtf */
3474 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3475 getText.codepage = 1200; /* no constant for unicode */
3476 getText.cb = MAX_BUF_LEN;
3477 getText.flags = GT_DEFAULT;
3478 getText.lpDefaultChar = NULL;
3479 getText.lpUsedDefChar = NULL;
3481 setText.flags = 0;
3482 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3483 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3484 ok(lstrcmpW(buf, TestItem1) == 0,
3485 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3487 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3488 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3489 p = (char *)buf;
3490 es.dwCookie = (DWORD_PTR)&p;
3491 es.dwError = 0;
3492 es.pfnCallback = test_WM_SETTEXT_esCallback;
3493 memset(buf, 0, sizeof(buf));
3494 SendMessage(hwndRichEdit, EM_STREAMOUT,
3495 (WPARAM)(SF_RTF), (LPARAM)&es);
3496 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3498 /* select some text */
3499 cr.cpMax = 1;
3500 cr.cpMin = 3;
3501 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3503 /* ST_SELECTION && !Unicode && \rtf */
3504 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3505 getText.codepage = 1200; /* no constant for unicode */
3506 getText.cb = MAX_BUF_LEN;
3507 getText.flags = GT_DEFAULT;
3508 getText.lpDefaultChar = NULL;
3509 getText.lpUsedDefChar = NULL;
3511 setText.flags = ST_SELECTION;
3512 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3513 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3514 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3516 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3517 setText.codepage = 1200; /* no constant for unicode */
3518 getText.codepage = CP_ACP;
3519 getText.cb = MAX_BUF_LEN;
3521 setText.flags = 0;
3522 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3523 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3525 /* select some text */
3526 cr.cpMax = 1;
3527 cr.cpMin = 3;
3528 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3530 /* ST_SELECTION && !Unicode && !\rtf */
3531 setText.codepage = CP_ACP;
3532 getText.codepage = 1200; /* no constant for unicode */
3533 getText.cb = MAX_BUF_LEN;
3534 getText.flags = GT_DEFAULT;
3535 getText.lpDefaultChar = NULL;
3536 getText.lpUsedDefChar = NULL;
3538 setText.flags = ST_SELECTION;
3539 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3540 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3541 ok(lstrcmpW(buf, TestItem1alt) == 0,
3542 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3543 " using ST_SELECTION and non-Unicode\n");
3545 /* Test setting text using rich text format */
3546 setText.flags = 0;
3547 setText.codepage = CP_ACP;
3548 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3549 getText.codepage = CP_ACP;
3550 getText.cb = MAX_BUF_LEN;
3551 getText.flags = GT_DEFAULT;
3552 getText.lpDefaultChar = NULL;
3553 getText.lpUsedDefChar = NULL;
3554 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3555 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3557 setText.flags = 0;
3558 setText.codepage = CP_ACP;
3559 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3560 getText.codepage = CP_ACP;
3561 getText.cb = MAX_BUF_LEN;
3562 getText.flags = GT_DEFAULT;
3563 getText.lpDefaultChar = NULL;
3564 getText.lpUsedDefChar = NULL;
3565 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3566 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3568 DestroyWindow(hwndRichEdit);
3571 static void test_EM_LIMITTEXT(void)
3573 int ret;
3575 HWND hwndRichEdit = new_richedit(NULL);
3577 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3578 * about setting the length to -1 for multiline edit controls doesn't happen.
3581 /* Don't check default gettextlimit case. That's done in other tests */
3583 /* Set textlimit to 100 */
3584 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3585 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3586 ok (ret == 100,
3587 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3589 /* Set textlimit to 0 */
3590 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3591 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3592 ok (ret == 65536,
3593 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3595 /* Set textlimit to -1 */
3596 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3597 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3598 ok (ret == -1,
3599 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3601 /* Set textlimit to -2 */
3602 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3603 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3604 ok (ret == -2,
3605 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3607 DestroyWindow (hwndRichEdit);
3611 static void test_EM_EXLIMITTEXT(void)
3613 int i, selBegin, selEnd, len1, len2;
3614 int result;
3615 char text[1024 + 1];
3616 char buffer[1024 + 1];
3617 int textlimit = 0; /* multiple of 100 */
3618 HWND hwndRichEdit = new_richedit(NULL);
3620 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3621 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3623 textlimit = 256000;
3624 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3625 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3626 /* set higher */
3627 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3629 textlimit = 1000;
3630 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3631 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3632 /* set lower */
3633 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3635 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3636 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3637 /* default for WParam = 0 */
3638 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3640 textlimit = sizeof(text)-1;
3641 memset(text, 'W', textlimit);
3642 text[sizeof(text)-1] = 0;
3643 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3644 /* maxed out text */
3645 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3647 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3648 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3649 len1 = selEnd - selBegin;
3651 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3652 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3653 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3654 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3655 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3656 len2 = selEnd - selBegin;
3658 ok(len1 != len2,
3659 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3660 len1,len2,i);
3662 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3663 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3664 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3665 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3666 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3667 len1 = selEnd - selBegin;
3669 ok(len1 != len2,
3670 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3671 len1,len2,i);
3673 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3674 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3675 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3676 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3677 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3678 len2 = selEnd - selBegin;
3680 ok(len1 == len2,
3681 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3682 len1,len2,i);
3684 /* set text up to the limit, select all the text, then add a char */
3685 textlimit = 5;
3686 memset(text, 'W', textlimit);
3687 text[textlimit] = 0;
3688 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3689 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3690 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3691 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3692 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3693 result = strcmp(buffer, "A");
3694 ok(0 == result, "got string = \"%s\"\n", buffer);
3696 /* WM_SETTEXT not limited */
3697 textlimit = 10;
3698 memset(text, 'W', textlimit);
3699 text[textlimit] = 0;
3700 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3701 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3702 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3703 i = strlen(buffer);
3704 ok(10 == i, "expected 10 chars\n");
3705 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3706 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3708 /* try inserting more text at end */
3709 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3710 ok(0 == i, "WM_CHAR wasn't processed\n");
3711 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3712 i = strlen(buffer);
3713 ok(10 == i, "expected 10 chars, got %i\n", i);
3714 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3715 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3717 /* try inserting text at beginning */
3718 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3719 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3720 ok(0 == i, "WM_CHAR wasn't processed\n");
3721 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3722 i = strlen(buffer);
3723 ok(10 == i, "expected 10 chars, got %i\n", i);
3724 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3725 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3727 /* WM_CHAR is limited */
3728 textlimit = 1;
3729 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3730 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3731 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3732 ok(0 == i, "WM_CHAR wasn't processed\n");
3733 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3734 ok(0 == i, "WM_CHAR wasn't processed\n");
3735 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3736 i = strlen(buffer);
3737 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3739 DestroyWindow(hwndRichEdit);
3742 static void test_EM_GETLIMITTEXT(void)
3744 int i;
3745 HWND hwndRichEdit = new_richedit(NULL);
3747 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3748 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3750 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3751 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3752 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3754 DestroyWindow(hwndRichEdit);
3757 static void test_WM_SETFONT(void)
3759 /* There is no invalid input or error conditions for this function.
3760 * NULL wParam and lParam just fall back to their default values
3761 * It should be noted that even if you use a gibberish name for your fonts
3762 * here, it will still work because the name is stored. They will display as
3763 * System, but will report their name to be whatever they were created as */
3765 HWND hwndRichEdit = new_richedit(NULL);
3766 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3767 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3768 FF_DONTCARE, "Marlett");
3769 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3770 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3771 FF_DONTCARE, "MS Sans Serif");
3772 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3773 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3774 FF_DONTCARE, "Courier");
3775 LOGFONTA sentLogFont;
3776 CHARFORMAT2A returnedCF2A;
3778 returnedCF2A.cbSize = sizeof(returnedCF2A);
3780 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3781 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
3782 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3784 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3785 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3786 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3787 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3789 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
3790 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3791 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3792 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3793 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3794 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3796 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
3797 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3798 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3799 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3800 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3801 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3803 /* This last test is special since we send in NULL. We clear the variables
3804 * and just compare to "System" instead of the sent in font name. */
3805 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3806 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3807 returnedCF2A.cbSize = sizeof(returnedCF2A);
3809 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
3810 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3811 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3812 ok (!strcmp("System",returnedCF2A.szFaceName),
3813 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3815 DestroyWindow(hwndRichEdit);
3819 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3820 LPBYTE pbBuff,
3821 LONG cb,
3822 LONG *pcb)
3824 const char** str = (const char**)dwCookie;
3825 int size = strlen(*str);
3826 if(size > 3) /* let's make it piecemeal for fun */
3827 size = 3;
3828 *pcb = cb;
3829 if (*pcb > size) {
3830 *pcb = size;
3832 if (*pcb > 0) {
3833 memcpy(pbBuff, *str, *pcb);
3834 *str += *pcb;
3836 return 0;
3839 static void test_EM_GETMODIFY(void)
3841 HWND hwndRichEdit = new_richedit(NULL);
3842 LRESULT result;
3843 SETTEXTEX setText;
3844 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3845 'S', 'o', 'm', 'e',
3846 'T', 'e', 'x', 't', 0};
3847 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3848 'S', 'o', 'm', 'e',
3849 'O', 't', 'h', 'e', 'r',
3850 'T', 'e', 'x', 't', 0};
3851 const char* streamText = "hello world";
3852 CHARFORMAT2 cf2;
3853 PARAFORMAT2 pf2;
3854 EDITSTREAM es;
3856 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3857 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3858 FF_DONTCARE, "Courier");
3860 setText.codepage = 1200; /* no constant for unicode */
3861 setText.flags = ST_KEEPUNDO;
3864 /* modify flag shouldn't be set when richedit is first created */
3865 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3866 ok (result == 0,
3867 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
3869 /* setting modify flag should actually set it */
3870 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
3871 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3872 ok (result != 0,
3873 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
3875 /* clearing modify flag should actually clear it */
3876 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3877 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3878 ok (result == 0,
3879 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
3881 /* setting font doesn't change modify flag */
3882 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3883 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
3884 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3885 ok (result == 0,
3886 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
3888 /* setting text should set modify flag */
3889 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3890 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3891 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3892 ok (result != 0,
3893 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
3895 /* undo previous text doesn't reset modify flag */
3896 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3897 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3898 ok (result != 0,
3899 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
3901 /* set text with no flag to keep undo stack should not set modify flag */
3902 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3903 setText.flags = 0;
3904 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3905 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3906 ok (result == 0,
3907 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
3909 /* WM_SETTEXT doesn't modify */
3910 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3911 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3912 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3913 ok (result == 0,
3914 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3916 /* clear the text */
3917 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3918 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3919 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3920 ok (result == 0,
3921 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3923 /* replace text */
3924 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3925 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3926 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3927 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3928 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3929 ok (result != 0,
3930 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3932 /* copy/paste text 1 */
3933 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3934 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3935 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3936 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3937 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3938 ok (result != 0,
3939 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3941 /* copy/paste text 2 */
3942 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3943 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3944 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3945 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3946 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3947 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3948 ok (result != 0,
3949 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3951 /* press char */
3952 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3953 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3954 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3955 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3956 ok (result != 0,
3957 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3959 /* press del */
3960 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3961 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3962 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
3963 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3964 ok (result != 0,
3965 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3967 /* set char format */
3968 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3969 cf2.cbSize = sizeof(CHARFORMAT2);
3970 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3971 (LPARAM) &cf2);
3972 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3973 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3974 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3975 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3976 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3977 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3978 ok (result != 0,
3979 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3981 /* set para format */
3982 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3983 pf2.cbSize = sizeof(PARAFORMAT2);
3984 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3985 (LPARAM) &pf2);
3986 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3987 pf2.wAlignment = PFA_RIGHT;
3988 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3989 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3990 ok (result == 0,
3991 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3993 /* EM_STREAM */
3994 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3995 es.dwCookie = (DWORD_PTR)&streamText;
3996 es.dwError = 0;
3997 es.pfnCallback = test_EM_GETMODIFY_esCallback;
3998 SendMessage(hwndRichEdit, EM_STREAMIN,
3999 (WPARAM)(SF_TEXT), (LPARAM)&es);
4000 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4001 ok (result != 0,
4002 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4004 DestroyWindow(hwndRichEdit);
4007 struct exsetsel_s {
4008 long min;
4009 long max;
4010 long expected_retval;
4011 int expected_getsel_start;
4012 int expected_getsel_end;
4013 int _exsetsel_todo_wine;
4014 int _getsel_todo_wine;
4017 const struct exsetsel_s exsetsel_tests[] = {
4018 /* sanity tests */
4019 {5, 10, 10, 5, 10, 0, 0},
4020 {15, 17, 17, 15, 17, 0, 0},
4021 /* test cpMax > strlen() */
4022 {0, 100, 18, 0, 18, 0, 1},
4023 /* test cpMin == cpMax */
4024 {5, 5, 5, 5, 5, 0, 0},
4025 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4026 {-1, 0, 5, 5, 5, 0, 0},
4027 {-1, 17, 5, 5, 5, 0, 0},
4028 {-1, 18, 5, 5, 5, 0, 0},
4029 /* test cpMin < 0 && cpMax < 0 */
4030 {-1, -1, 17, 17, 17, 0, 0},
4031 {-4, -5, 17, 17, 17, 0, 0},
4032 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4033 {0, -1, 18, 0, 18, 0, 1},
4034 {17, -5, 18, 17, 18, 0, 1},
4035 {18, -3, 17, 17, 17, 0, 0},
4036 /* test if cpMin > cpMax */
4037 {15, 19, 18, 15, 18, 0, 1},
4038 {19, 15, 18, 15, 18, 0, 1}
4041 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4042 CHARRANGE cr;
4043 long result;
4044 int start, end;
4046 cr.cpMin = setsel->min;
4047 cr.cpMax = setsel->max;
4048 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4050 if (setsel->_exsetsel_todo_wine) {
4051 todo_wine {
4052 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4054 } else {
4055 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4058 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4060 if (setsel->_getsel_todo_wine) {
4061 todo_wine {
4062 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);
4064 } else {
4065 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);
4069 static void test_EM_EXSETSEL(void)
4071 HWND hwndRichEdit = new_richedit(NULL);
4072 int i;
4073 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4075 /* sending some text to the window */
4076 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4077 /* 01234567890123456*/
4078 /* 10 */
4080 for (i = 0; i < num_tests; i++) {
4081 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4084 DestroyWindow(hwndRichEdit);
4087 static void test_EM_REPLACESEL(int redraw)
4089 HWND hwndRichEdit = new_richedit(NULL);
4090 char buffer[1024] = {0};
4091 int r;
4092 GETTEXTEX getText;
4093 CHARRANGE cr;
4095 /* sending some text to the window */
4096 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4097 /* 01234567890123456*/
4098 /* 10 */
4100 /* FIXME add more tests */
4101 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4102 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
4103 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4104 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4105 r = strcmp(buffer, "testing");
4106 ok(0 == r, "expected %d, got %d\n", 0, r);
4108 DestroyWindow(hwndRichEdit);
4110 hwndRichEdit = new_richedit(NULL);
4112 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4113 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4115 /* Test behavior with carriage returns and newlines */
4116 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4117 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4118 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4119 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4120 r = strcmp(buffer, "RichEdit1");
4121 ok(0 == r, "expected %d, got %d\n", 0, r);
4122 getText.cb = 1024;
4123 getText.codepage = CP_ACP;
4124 getText.flags = GT_DEFAULT;
4125 getText.lpDefaultChar = NULL;
4126 getText.lpUsedDefChar = NULL;
4127 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4128 ok(strcmp(buffer, "RichEdit1") == 0,
4129 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4131 /* Test number of lines reported after EM_REPLACESEL */
4132 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4133 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4135 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4136 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4137 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4138 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4139 r = strcmp(buffer, "RichEdit1\r\n");
4140 ok(0 == r, "expected %d, got %d\n", 0, r);
4141 getText.cb = 1024;
4142 getText.codepage = CP_ACP;
4143 getText.flags = GT_DEFAULT;
4144 getText.lpDefaultChar = NULL;
4145 getText.lpUsedDefChar = NULL;
4146 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4147 ok(strcmp(buffer, "RichEdit1\r") == 0,
4148 "EM_GETTEXTEX returned incorrect string\n");
4150 /* Test number of lines reported after EM_REPLACESEL */
4151 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4152 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4154 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4155 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4156 returns the number of characters *inserted* into the control (after
4157 required conversions), but WinXP's riched20 returns the number of
4158 characters interpreted from the original lParam. Wine's builtin riched20
4159 implements the WinXP behavior.
4161 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4162 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4163 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4164 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4166 /* Test number of lines reported after EM_REPLACESEL */
4167 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4168 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4170 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4171 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4172 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4173 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4175 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4176 r = strcmp(buffer, "RichEdit1\r\n");
4177 ok(0 == r, "expected %d, got %d\n", 0, r);
4178 getText.cb = 1024;
4179 getText.codepage = CP_ACP;
4180 getText.flags = GT_DEFAULT;
4181 getText.lpDefaultChar = NULL;
4182 getText.lpUsedDefChar = NULL;
4183 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4184 ok(strcmp(buffer, "RichEdit1\r") == 0,
4185 "EM_GETTEXTEX returned incorrect string\n");
4187 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4188 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4189 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4190 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4192 /* The following tests show that richedit should handle the special \r\r\n
4193 sequence by turning it into a single space on insertion. However,
4194 EM_REPLACESEL on WinXP returns the number of characters in the original
4195 string.
4198 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4199 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4200 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4201 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4202 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4203 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4204 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4206 /* Test the actual string */
4207 getText.cb = 1024;
4208 getText.codepage = CP_ACP;
4209 getText.flags = GT_DEFAULT;
4210 getText.lpDefaultChar = NULL;
4211 getText.lpUsedDefChar = NULL;
4212 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4213 ok(strcmp(buffer, "\r\r") == 0,
4214 "EM_GETTEXTEX returned incorrect string\n");
4216 /* Test number of lines reported after EM_REPLACESEL */
4217 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4218 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4220 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4221 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4222 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4223 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4224 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4225 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4226 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4227 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4229 /* Test the actual string */
4230 getText.cb = 1024;
4231 getText.codepage = CP_ACP;
4232 getText.flags = GT_DEFAULT;
4233 getText.lpDefaultChar = NULL;
4234 getText.lpUsedDefChar = NULL;
4235 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4236 ok(strcmp(buffer, " ") == 0,
4237 "EM_GETTEXTEX returned incorrect string\n");
4239 /* Test number of lines reported after EM_REPLACESEL */
4240 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4241 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4243 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4244 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4245 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4246 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4247 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4248 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4249 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4250 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4252 /* Test the actual string */
4253 getText.cb = 1024;
4254 getText.codepage = CP_ACP;
4255 getText.flags = GT_DEFAULT;
4256 getText.lpDefaultChar = NULL;
4257 getText.lpUsedDefChar = NULL;
4258 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4259 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4260 "EM_GETTEXTEX returned incorrect string\n");
4262 /* Test number of lines reported after EM_REPLACESEL */
4263 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4264 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4266 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4267 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4268 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4269 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4270 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4271 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4272 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4273 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4275 /* Test the actual string */
4276 getText.cb = 1024;
4277 getText.codepage = CP_ACP;
4278 getText.flags = GT_DEFAULT;
4279 getText.lpDefaultChar = NULL;
4280 getText.lpUsedDefChar = NULL;
4281 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4282 ok(strcmp(buffer, " \r") == 0,
4283 "EM_GETTEXTEX returned incorrect string\n");
4285 /* Test number of lines reported after EM_REPLACESEL */
4286 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4287 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4289 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4290 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4291 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4292 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4293 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4294 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4295 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4296 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4298 /* Test the actual string */
4299 getText.cb = 1024;
4300 getText.codepage = CP_ACP;
4301 getText.flags = GT_DEFAULT;
4302 getText.lpDefaultChar = NULL;
4303 getText.lpUsedDefChar = NULL;
4304 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4305 ok(strcmp(buffer, " \r\r") == 0,
4306 "EM_GETTEXTEX returned incorrect string\n");
4308 /* Test number of lines reported after EM_REPLACESEL */
4309 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4310 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4312 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4313 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4314 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4315 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4316 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4317 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4318 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4319 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4321 /* Test the actual string */
4322 getText.cb = 1024;
4323 getText.codepage = CP_ACP;
4324 getText.flags = GT_DEFAULT;
4325 getText.lpDefaultChar = NULL;
4326 getText.lpUsedDefChar = NULL;
4327 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4328 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4329 "EM_GETTEXTEX returned incorrect string\n");
4331 /* Test number of lines reported after EM_REPLACESEL */
4332 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4333 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4335 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4336 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4337 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4338 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4339 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4340 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4341 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4343 /* Test the actual string */
4344 getText.cb = 1024;
4345 getText.codepage = CP_ACP;
4346 getText.flags = GT_DEFAULT;
4347 getText.lpDefaultChar = NULL;
4348 getText.lpUsedDefChar = NULL;
4349 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4350 ok(strcmp(buffer, "\r\r") == 0,
4351 "EM_GETTEXTEX returned incorrect string\n");
4353 /* Test number of lines reported after EM_REPLACESEL */
4354 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4355 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4357 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4358 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4359 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4360 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4361 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4362 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4363 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4364 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4366 /* Test the actual string */
4367 getText.cb = 1024;
4368 getText.codepage = CP_ACP;
4369 getText.flags = GT_DEFAULT;
4370 getText.lpDefaultChar = NULL;
4371 getText.lpUsedDefChar = NULL;
4372 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4373 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4374 "EM_GETTEXTEX returned incorrect string\n");
4376 /* Test number of lines reported after EM_REPLACESEL */
4377 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4378 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4380 if (!redraw)
4381 /* This is needed to avoid interferring with keybd_event calls
4382 * on other tests that simulate keyboard events. */
4383 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4385 DestroyWindow(hwndRichEdit);
4388 static void test_WM_PASTE(void)
4390 int result;
4391 char buffer[1024] = {0};
4392 const char* text1 = "testing paste\r";
4393 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4394 const char* text1_after = "testing paste\r\n";
4395 const char* text2 = "testing paste\r\rtesting paste";
4396 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4397 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4398 HWND hwndRichEdit = new_richedit(NULL);
4400 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
4401 messages, probably because it inspects the keyboard state itself.
4402 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
4405 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4406 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4407 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4408 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4409 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4411 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4412 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4414 SEND_CTRL_C(hwndRichEdit); /* Copy */
4415 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4416 SEND_CTRL_V(hwndRichEdit); /* Paste */
4417 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4418 /* Pasted text should be visible at this step */
4419 result = strcmp(text1_step1, buffer);
4420 ok(result == 0,
4421 "test paste: strcmp = %i\n", result);
4422 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4423 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4424 /* Text should be the same as before (except for \r -> \r\n conversion) */
4425 result = strcmp(text1_after, buffer);
4426 ok(result == 0,
4427 "test paste: strcmp = %i\n", result);
4429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4430 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4431 SEND_CTRL_C(hwndRichEdit); /* Copy */
4432 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4433 SEND_CTRL_V(hwndRichEdit); /* Paste */
4434 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4435 /* Pasted text should be visible at this step */
4436 result = strcmp(text3, buffer);
4437 ok(result == 0,
4438 "test paste: strcmp = %i\n", result);
4439 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4440 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4441 /* Text should be the same as before (except for \r -> \r\n conversion) */
4442 result = strcmp(text2_after, buffer);
4443 ok(result == 0,
4444 "test paste: strcmp = %i\n", result);
4445 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4446 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4447 /* Text should revert to post-paste state */
4448 result = strcmp(buffer,text3);
4449 ok(result == 0,
4450 "test paste: strcmp = %i\n", result);
4452 DestroyWindow(hwndRichEdit);
4455 static void test_EM_FORMATRANGE(void)
4457 int r;
4458 FORMATRANGE fr;
4459 HDC hdc;
4460 HWND hwndRichEdit = new_richedit(NULL);
4462 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4464 hdc = GetDC(hwndRichEdit);
4465 ok(hdc != NULL, "Could not get HDC\n");
4467 fr.hdc = fr.hdcTarget = hdc;
4468 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4469 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4470 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4471 fr.chrg.cpMin = 0;
4472 fr.chrg.cpMax = 20;
4474 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4475 todo_wine {
4476 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4479 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4480 todo_wine {
4481 ok(r == 20 || r == 9, "EM_FORMATRANGE expect 20 or 9, got %d\n", r);
4484 fr.chrg.cpMin = 0;
4485 fr.chrg.cpMax = 10;
4487 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4488 todo_wine {
4489 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4492 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4493 todo_wine {
4494 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4497 DestroyWindow(hwndRichEdit);
4500 static int nCallbackCount = 0;
4502 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4503 LONG cb, LONG* pcb)
4505 const char text[] = {'t','e','s','t'};
4507 if (sizeof(text) <= cb)
4509 if ((int)dwCookie != nCallbackCount)
4511 *pcb = 0;
4512 return 0;
4515 memcpy (pbBuff, text, sizeof(text));
4516 *pcb = sizeof(text);
4518 nCallbackCount++;
4520 return 0;
4522 else
4523 return 1; /* indicates callback failed */
4526 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4527 LPBYTE pbBuff,
4528 LONG cb,
4529 LONG *pcb)
4531 const char** str = (const char**)dwCookie;
4532 int size = strlen(*str);
4533 *pcb = cb;
4534 if (*pcb > size) {
4535 *pcb = size;
4537 if (*pcb > 0) {
4538 memcpy(pbBuff, *str, *pcb);
4539 *str += *pcb;
4541 return 0;
4544 struct StringWithLength {
4545 int length;
4546 char *buffer;
4549 /* This callback is used to handled the null characters in a string. */
4550 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4551 LPBYTE pbBuff,
4552 LONG cb,
4553 LONG *pcb)
4555 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4556 int size = str->length;
4557 *pcb = cb;
4558 if (*pcb > size) {
4559 *pcb = size;
4561 if (*pcb > 0) {
4562 memcpy(pbBuff, str->buffer, *pcb);
4563 str->buffer += *pcb;
4564 str->length -= *pcb;
4566 return 0;
4569 static void test_EM_STREAMIN(void)
4571 HWND hwndRichEdit = new_richedit(NULL);
4572 LRESULT result;
4573 EDITSTREAM es;
4574 char buffer[1024] = {0};
4576 const char * streamText0 = "{\\rtf1 TestSomeText}";
4577 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4578 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4580 const char * streamText1 =
4581 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
4582 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
4583 "}\r\n";
4585 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4586 const char * streamText2 =
4587 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
4588 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
4589 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
4590 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
4591 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
4592 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
4593 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4595 const char * streamText3 = "RichEdit1";
4597 struct StringWithLength cookieForStream4;
4598 const char * streamText4 =
4599 "This text just needs to be long enough to cause run to be split onto "\
4600 "two separate lines and make sure the null terminating character is "\
4601 "handled properly.\0";
4602 int length4 = strlen(streamText4) + 1;
4603 cookieForStream4.buffer = (char *)streamText4;
4604 cookieForStream4.length = length4;
4606 /* Minimal test without \par at the end */
4607 es.dwCookie = (DWORD_PTR)&streamText0;
4608 es.dwError = 0;
4609 es.pfnCallback = test_EM_STREAMIN_esCallback;
4610 SendMessage(hwndRichEdit, EM_STREAMIN,
4611 (WPARAM)(SF_RTF), (LPARAM)&es);
4613 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4614 ok (result == 12,
4615 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4616 result = strcmp (buffer,"TestSomeText");
4617 ok (result == 0,
4618 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4619 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4621 /* Native richedit 2.0 ignores last \par */
4622 es.dwCookie = (DWORD_PTR)&streamText0a;
4623 es.dwError = 0;
4624 es.pfnCallback = test_EM_STREAMIN_esCallback;
4625 SendMessage(hwndRichEdit, EM_STREAMIN,
4626 (WPARAM)(SF_RTF), (LPARAM)&es);
4628 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4629 ok (result == 12,
4630 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4631 result = strcmp (buffer,"TestSomeText");
4632 ok (result == 0,
4633 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4634 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4636 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4637 es.dwCookie = (DWORD_PTR)&streamText0b;
4638 es.dwError = 0;
4639 es.pfnCallback = test_EM_STREAMIN_esCallback;
4640 SendMessage(hwndRichEdit, EM_STREAMIN,
4641 (WPARAM)(SF_RTF), (LPARAM)&es);
4643 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4644 ok (result == 14,
4645 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4646 result = strcmp (buffer,"TestSomeText\r\n");
4647 ok (result == 0,
4648 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4649 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4651 es.dwCookie = (DWORD_PTR)&streamText1;
4652 es.dwError = 0;
4653 es.pfnCallback = test_EM_STREAMIN_esCallback;
4654 SendMessage(hwndRichEdit, EM_STREAMIN,
4655 (WPARAM)(SF_RTF), (LPARAM)&es);
4657 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4658 ok (result == 12,
4659 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4660 result = strcmp (buffer,"TestSomeText");
4661 ok (result == 0,
4662 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4663 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4665 es.dwCookie = (DWORD_PTR)&streamText2;
4666 es.dwError = 0;
4667 SendMessage(hwndRichEdit, EM_STREAMIN,
4668 (WPARAM)(SF_RTF), (LPARAM)&es);
4670 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4671 ok (result == 0,
4672 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4673 ok (strlen(buffer) == 0,
4674 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4675 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4677 es.dwCookie = (DWORD_PTR)&streamText3;
4678 es.dwError = 0;
4679 SendMessage(hwndRichEdit, EM_STREAMIN,
4680 (WPARAM)(SF_RTF), (LPARAM)&es);
4682 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4683 ok (result == 0,
4684 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4685 ok (strlen(buffer) == 0,
4686 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4687 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4689 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4690 es.dwError = 0;
4691 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4692 SendMessage(hwndRichEdit, EM_STREAMIN,
4693 (WPARAM)(SF_TEXT), (LPARAM)&es);
4695 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4696 ok (result == length4,
4697 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4698 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4700 DestroyWindow(hwndRichEdit);
4703 static void test_EM_StreamIn_Undo(void)
4705 /* The purpose of this test is to determine when a EM_StreamIn should be
4706 * undoable. This is important because WM_PASTE currently uses StreamIn and
4707 * pasting should always be undoable but streaming isn't always.
4709 * cases to test:
4710 * StreamIn plain text without SFF_SELECTION.
4711 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4712 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4713 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4714 * Feel free to add tests for other text modes or StreamIn things.
4718 HWND hwndRichEdit = new_richedit(NULL);
4719 LRESULT result;
4720 EDITSTREAM es;
4721 char buffer[1024] = {0};
4722 const char randomtext[] = "Some text";
4724 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4726 /* StreamIn, no SFF_SELECTION */
4727 es.dwCookie = nCallbackCount;
4728 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4729 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4730 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4731 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4732 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4733 result = strcmp (buffer,"test");
4734 ok (result == 0,
4735 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4737 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4738 ok (result == FALSE,
4739 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4741 /* StreamIn, SFF_SELECTION, but nothing selected */
4742 es.dwCookie = nCallbackCount;
4743 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4744 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4745 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4746 SendMessage(hwndRichEdit, EM_STREAMIN,
4747 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4748 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4749 result = strcmp (buffer,"testSome text");
4750 ok (result == 0,
4751 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4753 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4754 ok (result == TRUE,
4755 "EM_STREAMIN with SFF_SELECTION but no selection set "
4756 "should create an undo\n");
4758 /* StreamIn, SFF_SELECTION, with a selection */
4759 es.dwCookie = nCallbackCount;
4760 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4761 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4762 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
4763 SendMessage(hwndRichEdit, EM_STREAMIN,
4764 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4765 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4766 result = strcmp (buffer,"Sometesttext");
4767 ok (result == 0,
4768 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4770 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4771 ok (result == TRUE,
4772 "EM_STREAMIN with SFF_SELECTION and selection set "
4773 "should create an undo\n");
4775 DestroyWindow(hwndRichEdit);
4778 static BOOL is_em_settextex_supported(HWND hwnd)
4780 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
4781 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
4784 static void test_unicode_conversions(void)
4786 static const WCHAR tW[] = {'t',0};
4787 static const WCHAR teW[] = {'t','e',0};
4788 static const WCHAR textW[] = {'t','e','s','t',0};
4789 static const char textA[] = "test";
4790 char bufA[64];
4791 WCHAR bufW[64];
4792 HWND hwnd;
4793 int is_win9x, em_settextex_supported, ret;
4795 is_win9x = GetVersion() & 0x80000000;
4797 #define set_textA(hwnd, wm_set_text, txt) \
4798 do { \
4799 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
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 = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4803 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
4804 } while(0)
4805 #define expect_textA(hwnd, wm_get_text, txt) \
4806 do { \
4807 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, 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(bufA, 0xAA, sizeof(bufA)); \
4811 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4812 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4813 ret = lstrcmpA(bufA, txt); \
4814 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
4815 } while(0)
4817 #define set_textW(hwnd, wm_set_text, txt) \
4818 do { \
4819 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
4820 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4821 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4822 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4823 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
4824 } while(0)
4825 #define expect_textW(hwnd, wm_get_text, txt) \
4826 do { \
4827 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, 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(bufW, 0xAA, sizeof(bufW)); \
4831 if (is_win9x) \
4833 assert(wm_get_text == EM_GETTEXTEX); \
4834 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4835 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4837 else \
4839 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4840 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
4842 ret = lstrcmpW(bufW, txt); \
4843 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
4844 } while(0)
4845 #define expect_empty(hwnd, wm_get_text) \
4846 do { \
4847 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4848 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4849 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4850 memset(bufA, 0xAA, sizeof(bufA)); \
4851 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4852 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
4853 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
4854 } while(0)
4856 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4857 0, 0, 200, 60, 0, 0, 0, 0);
4858 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4860 ret = IsWindowUnicode(hwnd);
4861 if (is_win9x)
4862 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
4863 else
4864 ok(ret, "RichEdit20W should be unicode under NT\n");
4866 /* EM_SETTEXTEX is supported starting from version 3.0 */
4867 em_settextex_supported = is_em_settextex_supported(hwnd);
4868 trace("EM_SETTEXTEX is %ssupported on this platform\n",
4869 em_settextex_supported ? "" : "NOT ");
4871 expect_empty(hwnd, WM_GETTEXT);
4872 expect_empty(hwnd, EM_GETTEXTEX);
4874 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
4875 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4876 expect_textA(hwnd, WM_GETTEXT, "t");
4877 expect_textA(hwnd, EM_GETTEXTEX, "t");
4878 expect_textW(hwnd, EM_GETTEXTEX, tW);
4880 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
4881 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4882 expect_textA(hwnd, WM_GETTEXT, "te");
4883 expect_textA(hwnd, EM_GETTEXTEX, "te");
4884 expect_textW(hwnd, EM_GETTEXTEX, teW);
4886 set_textA(hwnd, WM_SETTEXT, NULL);
4887 expect_empty(hwnd, WM_GETTEXT);
4888 expect_empty(hwnd, EM_GETTEXTEX);
4890 if (is_win9x)
4891 set_textA(hwnd, WM_SETTEXT, textW);
4892 else
4893 set_textA(hwnd, WM_SETTEXT, textA);
4894 expect_textA(hwnd, WM_GETTEXT, textA);
4895 expect_textA(hwnd, EM_GETTEXTEX, textA);
4896 expect_textW(hwnd, EM_GETTEXTEX, textW);
4898 if (em_settextex_supported)
4900 set_textA(hwnd, EM_SETTEXTEX, textA);
4901 expect_textA(hwnd, WM_GETTEXT, textA);
4902 expect_textA(hwnd, EM_GETTEXTEX, textA);
4903 expect_textW(hwnd, EM_GETTEXTEX, textW);
4906 if (!is_win9x)
4908 set_textW(hwnd, WM_SETTEXT, textW);
4909 expect_textW(hwnd, WM_GETTEXT, textW);
4910 expect_textA(hwnd, WM_GETTEXT, textA);
4911 expect_textW(hwnd, EM_GETTEXTEX, textW);
4912 expect_textA(hwnd, EM_GETTEXTEX, textA);
4914 if (em_settextex_supported)
4916 set_textW(hwnd, EM_SETTEXTEX, textW);
4917 expect_textW(hwnd, WM_GETTEXT, textW);
4918 expect_textA(hwnd, WM_GETTEXT, textA);
4919 expect_textW(hwnd, EM_GETTEXTEX, textW);
4920 expect_textA(hwnd, EM_GETTEXTEX, textA);
4923 DestroyWindow(hwnd);
4925 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
4926 0, 0, 200, 60, 0, 0, 0, 0);
4927 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4929 ret = IsWindowUnicode(hwnd);
4930 ok(!ret, "RichEdit20A should NOT be unicode\n");
4932 set_textA(hwnd, WM_SETTEXT, textA);
4933 expect_textA(hwnd, WM_GETTEXT, textA);
4934 expect_textA(hwnd, EM_GETTEXTEX, textA);
4935 expect_textW(hwnd, EM_GETTEXTEX, textW);
4937 if (em_settextex_supported)
4939 set_textA(hwnd, EM_SETTEXTEX, textA);
4940 expect_textA(hwnd, WM_GETTEXT, textA);
4941 expect_textA(hwnd, EM_GETTEXTEX, textA);
4942 expect_textW(hwnd, EM_GETTEXTEX, textW);
4945 if (!is_win9x)
4947 set_textW(hwnd, WM_SETTEXT, textW);
4948 expect_textW(hwnd, WM_GETTEXT, textW);
4949 expect_textA(hwnd, WM_GETTEXT, textA);
4950 expect_textW(hwnd, EM_GETTEXTEX, textW);
4951 expect_textA(hwnd, EM_GETTEXTEX, textA);
4953 if (em_settextex_supported)
4955 set_textW(hwnd, EM_SETTEXTEX, textW);
4956 expect_textW(hwnd, WM_GETTEXT, textW);
4957 expect_textA(hwnd, WM_GETTEXT, textA);
4958 expect_textW(hwnd, EM_GETTEXTEX, textW);
4959 expect_textA(hwnd, EM_GETTEXTEX, textA);
4962 DestroyWindow(hwnd);
4965 static void test_WM_CHAR(void)
4967 HWND hwnd;
4968 int ret;
4969 const char * char_list = "abc\rabc\r";
4970 const char * expected_content_single = "abcabc";
4971 const char * expected_content_multi = "abc\r\nabc\r\n";
4972 char buffer[64] = {0};
4973 const char * p;
4975 /* single-line control must IGNORE carriage returns */
4976 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
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_single);
4991 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4993 DestroyWindow(hwnd);
4995 /* multi-line control inserts CR normally */
4996 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4997 0, 0, 200, 60, 0, 0, 0, 0);
4998 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5000 p = char_list;
5001 while (*p != '\0') {
5002 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5003 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5004 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5005 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5006 p++;
5009 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5010 ret = strcmp(buffer, expected_content_multi);
5011 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5013 DestroyWindow(hwnd);
5016 static void test_EM_GETTEXTLENGTHEX(void)
5018 HWND hwnd;
5019 GETTEXTLENGTHEX gtl;
5020 int ret;
5021 const char * base_string = "base string";
5022 const char * test_string = "a\nb\n\n\r\n";
5023 const char * test_string_after = "a";
5024 const char * test_string_2 = "a\rtest\rstring";
5025 char buffer[64] = {0};
5027 /* single line */
5028 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5029 0, 0, 200, 60, 0, 0, 0, 0);
5030 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5032 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5033 gtl.codepage = CP_ACP;
5034 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5035 ok(ret == 0, "ret %d\n",ret);
5037 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5038 gtl.codepage = CP_ACP;
5039 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5040 ok(ret == 0, "ret %d\n",ret);
5042 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5044 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5045 gtl.codepage = CP_ACP;
5046 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5047 ok(ret == strlen(base_string), "ret %d\n",ret);
5049 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5050 gtl.codepage = CP_ACP;
5051 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5052 ok(ret == strlen(base_string), "ret %d\n",ret);
5054 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5056 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5057 gtl.codepage = CP_ACP;
5058 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5059 ok(ret == 1, "ret %d\n",ret);
5061 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5062 gtl.codepage = CP_ACP;
5063 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5064 ok(ret == 1, "ret %d\n",ret);
5066 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5067 ret = strcmp(buffer, test_string_after);
5068 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5070 DestroyWindow(hwnd);
5072 /* multi line */
5073 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5074 0, 0, 200, 60, 0, 0, 0, 0);
5075 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5077 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5078 gtl.codepage = CP_ACP;
5079 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5080 ok(ret == 0, "ret %d\n",ret);
5082 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5083 gtl.codepage = CP_ACP;
5084 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5085 ok(ret == 0, "ret %d\n",ret);
5087 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5089 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5090 gtl.codepage = CP_ACP;
5091 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5092 ok(ret == strlen(base_string), "ret %d\n",ret);
5094 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5095 gtl.codepage = CP_ACP;
5096 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5097 ok(ret == strlen(base_string), "ret %d\n",ret);
5099 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5101 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5102 gtl.codepage = CP_ACP;
5103 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5104 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5106 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5107 gtl.codepage = CP_ACP;
5108 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5109 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5111 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5113 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5114 gtl.codepage = CP_ACP;
5115 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5116 ok(ret == 10, "ret %d\n",ret);
5118 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5119 gtl.codepage = CP_ACP;
5120 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5121 ok(ret == 6, "ret %d\n",ret);
5123 DestroyWindow(hwnd);
5127 /* globals that parent and child access when checking event masks & notifications */
5128 static HWND eventMaskEditHwnd = 0;
5129 static int queriedEventMask;
5130 static int watchForEventMask = 0;
5132 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5133 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5135 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5137 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5139 return DefWindowProcA(hwnd, message, wParam, lParam);
5142 /* test event masks in combination with WM_COMMAND */
5143 static void test_eventMask(void)
5145 HWND parent;
5146 int ret;
5147 WNDCLASSA cls;
5148 const char text[] = "foo bar\n";
5149 int eventMask;
5151 /* register class to capture WM_COMMAND */
5152 cls.style = 0;
5153 cls.lpfnWndProc = ParentMsgCheckProcA;
5154 cls.cbClsExtra = 0;
5155 cls.cbWndExtra = 0;
5156 cls.hInstance = GetModuleHandleA(0);
5157 cls.hIcon = 0;
5158 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5159 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5160 cls.lpszMenuName = NULL;
5161 cls.lpszClassName = "EventMaskParentClass";
5162 if(!RegisterClassA(&cls)) assert(0);
5164 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5165 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5166 ok (parent != 0, "Failed to create parent window\n");
5168 eventMaskEditHwnd = new_richedit(parent);
5169 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5171 eventMask = ENM_CHANGE | ENM_UPDATE;
5172 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5173 ok(ret == ENM_NONE, "wrong event mask\n");
5174 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5175 ok(ret == eventMask, "failed to set event mask\n");
5177 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5178 queriedEventMask = 0; /* initialize to something other than we expect */
5179 watchForEventMask = EN_CHANGE;
5180 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5181 ok(ret == TRUE, "failed to set text\n");
5182 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5183 notification in response to WM_SETTEXT */
5184 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5185 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5187 DestroyWindow(parent);
5190 static int received_WM_NOTIFY = 0;
5191 static int modify_at_WM_NOTIFY = 0;
5192 static HWND hwndRichedit_WM_NOTIFY;
5194 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5196 if(message == WM_NOTIFY)
5198 received_WM_NOTIFY = 1;
5199 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5201 return DefWindowProcA(hwnd, message, wParam, lParam);
5204 static void test_WM_NOTIFY(void)
5206 HWND parent;
5207 WNDCLASSA cls;
5208 CHARFORMAT2 cf2;
5210 /* register class to capture WM_NOTIFY */
5211 cls.style = 0;
5212 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5213 cls.cbClsExtra = 0;
5214 cls.cbWndExtra = 0;
5215 cls.hInstance = GetModuleHandleA(0);
5216 cls.hIcon = 0;
5217 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5218 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5219 cls.lpszMenuName = NULL;
5220 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5221 if(!RegisterClassA(&cls)) assert(0);
5223 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5224 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5225 ok (parent != 0, "Failed to create parent window\n");
5227 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5228 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5230 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5232 /* Notifications for selection change should only be sent when selection
5233 actually changes. EM_SETCHARFORMAT is one message that calls
5234 ME_CommitUndo, which should check whether message should be sent */
5235 received_WM_NOTIFY = 0;
5236 cf2.cbSize = sizeof(CHARFORMAT2);
5237 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5238 (LPARAM) &cf2);
5239 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5240 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5241 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5242 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5244 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5245 already at 0. */
5246 received_WM_NOTIFY = 0;
5247 modify_at_WM_NOTIFY = 0;
5248 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5249 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5250 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5252 received_WM_NOTIFY = 0;
5253 modify_at_WM_NOTIFY = 0;
5254 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5255 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5257 received_WM_NOTIFY = 0;
5258 modify_at_WM_NOTIFY = 0;
5259 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5260 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5261 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5263 DestroyWindow(hwndRichedit_WM_NOTIFY);
5264 DestroyWindow(parent);
5267 static void test_undo_coalescing(void)
5269 HWND hwnd;
5270 int result;
5271 char buffer[64] = {0};
5273 /* multi-line control inserts CR normally */
5274 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5275 0, 0, 200, 60, 0, 0, 0, 0);
5276 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5278 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5279 ok (result == FALSE, "Can undo after window creation.\n");
5280 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5281 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5282 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5283 ok (result == FALSE, "Can redo after window creation.\n");
5284 result = SendMessage(hwnd, EM_REDO, 0, 0);
5285 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5287 /* Test the effect of arrows keys during typing on undo transactions*/
5288 simulate_typing_characters(hwnd, "one two three");
5289 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5290 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5291 simulate_typing_characters(hwnd, " four five six");
5293 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5294 ok (result == FALSE, "Can redo before anything is undone.\n");
5295 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5296 ok (result == TRUE, "Cannot undo typed characters.\n");
5297 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5298 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5299 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5300 ok (result == TRUE, "Cannot redo after undo.\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 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5306 ok (result == TRUE, "Cannot undo typed characters.\n");
5307 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5308 ok (result == TRUE, "Failed to undo typed characters.\n");
5309 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5310 result = strcmp(buffer, "");
5311 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5313 /* Test the effect of focus changes during typing on undo transactions*/
5314 simulate_typing_characters(hwnd, "one two three");
5315 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5316 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5317 SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
5318 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
5319 simulate_typing_characters(hwnd, " four five six");
5320 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5321 ok (result == TRUE, "Failed to undo typed characters.\n");
5322 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5323 result = strcmp(buffer, "one two three");
5324 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5326 /* Test the effect of the back key during typing on undo transactions */
5327 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5328 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5329 ok (result == TRUE, "Failed to clear the text.\n");
5330 simulate_typing_characters(hwnd, "one two threa");
5331 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5332 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5333 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5334 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5335 simulate_typing_characters(hwnd, "e four five six");
5336 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5337 ok (result == TRUE, "Failed to undo typed characters.\n");
5338 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5339 result = strcmp(buffer, "");
5340 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5342 /* Test the effect of the delete key during typing on undo transactions */
5343 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5344 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5345 ok(result == TRUE, "Failed to set the text.\n");
5346 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5347 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5348 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5349 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5350 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5351 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5352 ok (result == TRUE, "Failed to undo typed characters.\n");
5353 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5354 result = strcmp(buffer, "acd");
5355 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5356 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5357 ok (result == TRUE, "Failed to undo typed characters.\n");
5358 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5359 result = strcmp(buffer, "abcd");
5360 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5362 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5363 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5364 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5365 ok (result == TRUE, "Failed to clear the text.\n");
5366 simulate_typing_characters(hwnd, "one two three");
5367 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5368 ok (result == 0, "expected %d but got %d\n", 0, result);
5369 simulate_typing_characters(hwnd, " four five six");
5370 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5371 ok (result == TRUE, "Failed to undo typed characters.\n");
5372 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5373 result = strcmp(buffer, "one two three");
5374 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5375 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5376 ok (result == TRUE, "Failed to undo typed characters.\n");
5377 ok (result == TRUE, "Failed to undo typed characters.\n");
5378 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5379 result = strcmp(buffer, "");
5380 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5382 DestroyWindow(hwnd);
5385 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5386 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5388 static void test_word_movement(void)
5390 HWND hwnd;
5391 int result;
5392 int sel_start, sel_end;
5394 /* multi-line control inserts CR normally */
5395 hwnd = new_richedit(NULL);
5397 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5398 ok (result == TRUE, "Failed to clear the text.\n");
5399 SendMessage(hwnd, EM_SETSEL, 0, 0);
5400 /* |one two three */
5402 SEND_CTRL_RIGHT(hwnd);
5403 /* one |two three */
5404 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5405 ok(sel_start == sel_end, "Selection should be empty\n");
5406 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5408 SEND_CTRL_RIGHT(hwnd);
5409 /* one two |three */
5410 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5411 ok(sel_start == sel_end, "Selection should be empty\n");
5412 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5414 SEND_CTRL_LEFT(hwnd);
5415 /* one |two three */
5416 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5417 ok(sel_start == sel_end, "Selection should be empty\n");
5418 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5420 SEND_CTRL_LEFT(hwnd);
5421 /* |one two three */
5422 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5423 ok(sel_start == sel_end, "Selection should be empty\n");
5424 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5426 SendMessage(hwnd, EM_SETSEL, 8, 8);
5427 /* one two | three */
5428 SEND_CTRL_RIGHT(hwnd);
5429 /* one two |three */
5430 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5431 ok(sel_start == sel_end, "Selection should be empty\n");
5432 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5434 SendMessage(hwnd, EM_SETSEL, 11, 11);
5435 /* one two th|ree */
5436 SEND_CTRL_LEFT(hwnd);
5437 /* one two |three */
5438 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5439 ok(sel_start == sel_end, "Selection should be empty\n");
5440 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5442 DestroyWindow(hwnd);
5445 static void test_EM_CHARFROMPOS(void)
5447 HWND hwnd;
5448 int result;
5449 POINTL point;
5450 point.x = 0;
5451 point.y = 50;
5453 /* multi-line control inserts CR normally */
5454 hwnd = new_richedit(NULL);
5455 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5456 (LPARAM)"one two three four five six seven");
5458 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5459 ok(result == 0, "expected character index of 0 but got %d\n", result);
5461 DestroyWindow(hwnd);
5464 static void test_word_wrap(void)
5466 HWND hwnd;
5467 POINTL point = {0, 60}; /* This point must be below the first line */
5468 const char *text = "Must be long enough to test line wrapping";
5469 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
5470 int res, pos;
5472 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
5473 * when specified on window creation and set later. */
5474 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5475 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5476 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5477 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5478 ok(res, "WM_SETTEXT failed.\n");
5479 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5480 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5482 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
5483 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5484 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5485 DestroyWindow(hwnd);
5487 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
5488 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5489 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5491 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5492 ok(res, "WM_SETTEXT failed.\n");
5493 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5494 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5496 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5497 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5498 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5499 DestroyWindow(hwnd);
5501 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
5502 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5503 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5504 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5505 ok(res, "WM_SETTEXT failed.\n");
5506 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5507 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5509 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5510 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5511 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5512 DestroyWindow(hwnd);
5514 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
5515 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
5516 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5517 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5518 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5519 ok(res, "WM_SETTEXT failed.\n");
5520 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5521 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5523 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5524 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5525 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5527 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
5528 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
5529 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5530 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5531 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5533 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
5534 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5535 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5536 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5537 DestroyWindow(hwnd);
5540 START_TEST( editor )
5542 /* Must explicitly LoadLibrary(). The test has no references to functions in
5543 * RICHED20.DLL, so the linker doesn't actually link to it. */
5544 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
5545 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
5546 test_WM_CHAR();
5547 test_EM_FINDTEXT();
5548 test_EM_GETLINE();
5549 test_EM_POSFROMCHAR();
5550 test_EM_SCROLLCARET();
5551 test_EM_SCROLL();
5552 test_scrollbar_visibility();
5553 test_WM_SETTEXT();
5554 test_EM_LINELENGTH();
5555 test_EM_SETCHARFORMAT();
5556 test_EM_SETTEXTMODE();
5557 test_TM_PLAINTEXT();
5558 test_EM_SETOPTIONS();
5559 test_WM_GETTEXT();
5560 test_EM_GETTEXTRANGE();
5561 test_EM_GETSELTEXT();
5562 test_EM_SETUNDOLIMIT();
5563 test_ES_PASSWORD();
5564 test_EM_SETTEXTEX();
5565 test_EM_LIMITTEXT();
5566 test_EM_EXLIMITTEXT();
5567 test_EM_GETLIMITTEXT();
5568 test_WM_SETFONT();
5569 test_EM_GETMODIFY();
5570 test_EM_EXSETSEL();
5571 test_WM_PASTE();
5572 test_EM_STREAMIN();
5573 test_EM_STREAMOUT();
5574 test_EM_StreamIn_Undo();
5575 test_EM_FORMATRANGE();
5576 test_unicode_conversions();
5577 test_EM_GETTEXTLENGTHEX();
5578 test_EM_REPLACESEL(1);
5579 test_EM_REPLACESEL(0);
5580 test_WM_NOTIFY();
5581 test_EM_AUTOURLDETECT();
5582 test_eventMask();
5583 test_undo_coalescing();
5584 test_word_movement();
5585 test_EM_CHARFROMPOS();
5586 test_SETPARAFORMAT();
5587 test_word_wrap();
5589 /* Set the environment variable WINETEST_RICHED20 to keep windows
5590 * responsive and open for 30 seconds. This is useful for debugging.
5592 if (getenv( "WINETEST_RICHED20" )) {
5593 keep_responsive(30);
5596 OleFlushClipboard();
5597 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());