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