richedit: Fixed simulated keyboard events on tests.
[wine.git] / dlls / riched20 / tests / editor.c
blob0776fe47f14f089a9922f20027fafd9e25c88d5d
1 /*
2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <assert.h>
25 #include <windef.h>
26 #include <winbase.h>
27 #include <wingdi.h>
28 #include <winuser.h>
29 #include <winnls.h>
30 #include <ole2.h>
31 #include <richedit.h>
32 #include <time.h>
33 #include <wine/test.h>
35 static HMODULE hmoduleRichEdit;
37 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
38 HWND hwnd;
39 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
40 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
41 hmoduleRichEdit, NULL);
42 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
43 return hwnd;
46 static HWND new_richedit(HWND parent) {
47 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
50 static void processPendingMessages(void)
52 MSG msg;
53 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
54 TranslateMessage(&msg);
55 DispatchMessage(&msg);
59 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
61 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
62 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
63 SetFocus(hwnd);
64 keybd_event(mod_vk, mod_scan_code, 0, 0);
65 keybd_event(vk, scan_code, 0, 0);
66 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
67 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
68 processPendingMessages();
71 static void simulate_typing_characters(HWND hwnd, const char* szChars)
73 int ret;
75 while (*szChars != '\0') {
76 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
77 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
78 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
79 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
80 szChars++;
84 static const char haystack[] = "WINEWine wineWine wine WineWine";
85 /* ^0 ^10 ^20 ^30 */
87 struct find_s {
88 int start;
89 int end;
90 const char *needle;
91 int flags;
92 int expected_loc;
93 int _todo_wine;
97 struct find_s find_tests[] = {
98 /* Find in empty text */
99 {0, -1, "foo", FR_DOWN, -1, 0},
100 {0, -1, "foo", 0, -1, 0},
101 {0, -1, "", FR_DOWN, -1, 0},
102 {20, 5, "foo", FR_DOWN, -1, 0},
103 {5, 20, "foo", FR_DOWN, -1, 0}
106 struct find_s find_tests2[] = {
107 /* No-result find */
108 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
109 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
111 /* Subsequent finds */
112 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
113 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
114 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
115 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
117 /* Find backwards */
118 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
119 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
120 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
122 /* Case-insensitive */
123 {1, 31, "wInE", FR_DOWN, 4, 0},
124 {1, 31, "Wine", FR_DOWN, 4, 0},
126 /* High-to-low ranges */
127 {20, 5, "Wine", FR_DOWN, -1, 0},
128 {2, 1, "Wine", FR_DOWN, -1, 0},
129 {30, 29, "Wine", FR_DOWN, -1, 0},
130 {20, 5, "Wine", 0, 13, 0},
132 /* Find nothing */
133 {5, 10, "", FR_DOWN, -1, 0},
134 {10, 5, "", FR_DOWN, -1, 0},
135 {0, -1, "", FR_DOWN, -1, 0},
136 {10, 5, "", 0, -1, 0},
138 /* Whole-word search */
139 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
140 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
141 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
142 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
143 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
144 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
145 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
147 /* Bad ranges */
148 {5, 200, "XXX", FR_DOWN, -1, 0},
149 {-20, 20, "Wine", FR_DOWN, -1, 0},
150 {-20, 20, "Wine", FR_DOWN, -1, 0},
151 {-15, -20, "Wine", FR_DOWN, -1, 0},
152 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
154 /* Check the case noted in bug 4479 where matches at end aren't recognized */
155 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
156 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
157 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
158 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
159 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
161 /* The backwards case of bug 4479; bounds look right
162 * Fails because backward find is wrong */
163 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
164 {0, 20, "WINE", FR_MATCHCASE, -1, 0},
166 {0, -1, "wineWine wine", 0, -1, 0},
169 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
170 int findloc;
171 FINDTEXT ft;
172 memset(&ft, 0, sizeof(ft));
173 ft.chrg.cpMin = f->start;
174 ft.chrg.cpMax = f->end;
175 ft.lpstrText = f->needle;
176 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
177 ok(findloc == f->expected_loc,
178 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
179 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
182 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
183 int id) {
184 int findloc;
185 FINDTEXTEX ft;
186 int expected_end_loc;
188 memset(&ft, 0, sizeof(ft));
189 ft.chrg.cpMin = f->start;
190 ft.chrg.cpMax = f->end;
191 ft.lpstrText = f->needle;
192 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
193 ok(findloc == f->expected_loc,
194 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
195 name, id, f->needle, f->start, f->end, f->flags, findloc);
196 ok(ft.chrgText.cpMin == f->expected_loc,
197 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
198 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
199 expected_end_loc = ((f->expected_loc == -1) ? -1
200 : f->expected_loc + strlen(f->needle));
201 ok(ft.chrgText.cpMax == expected_end_loc,
202 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
203 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
206 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
207 int num_tests)
209 int i;
211 for (i = 0; i < num_tests; i++) {
212 if (find[i]._todo_wine) {
213 todo_wine {
214 check_EM_FINDTEXT(hwnd, name, &find[i], i);
215 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
217 } else {
218 check_EM_FINDTEXT(hwnd, name, &find[i], i);
219 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
224 static void test_EM_FINDTEXT(void)
226 HWND hwndRichEdit = new_richedit(NULL);
227 CHARFORMAT2 cf2;
229 /* Empty rich edit control */
230 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
231 sizeof(find_tests)/sizeof(struct find_s));
233 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
235 /* Haystack text */
236 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
237 sizeof(find_tests2)/sizeof(struct find_s));
239 /* Setting a format on an arbitrary range should have no effect in search
240 results. This tests correct offset reporting across runs. */
241 cf2.cbSize = sizeof(CHARFORMAT2);
242 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
243 (LPARAM) &cf2);
244 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
245 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
246 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
247 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
249 /* Haystack text, again */
250 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
251 sizeof(find_tests2)/sizeof(struct find_s));
253 /* Yet another range */
254 cf2.dwMask = CFM_BOLD | cf2.dwMask;
255 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
256 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
257 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
259 /* Haystack text, again */
260 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
261 sizeof(find_tests2)/sizeof(struct find_s));
263 DestroyWindow(hwndRichEdit);
266 static const struct getline_s {
267 int line;
268 size_t buffer_len;
269 const char *text;
270 } gl[] = {
271 {0, 10, "foo bar\r"},
272 {1, 10, "\r"},
273 {2, 10, "bar\r"},
274 {3, 10, "\r"},
276 /* Buffer smaller than line length */
277 {0, 2, "foo bar\r"},
278 {0, 1, "foo bar\r"},
279 {0, 0, "foo bar\r"}
282 static void test_EM_GETLINE(void)
284 int i;
285 HWND hwndRichEdit = new_richedit(NULL);
286 static const int nBuf = 1024;
287 char dest[1024], origdest[1024];
288 const char text[] = "foo bar\n"
289 "\n"
290 "bar\n";
292 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
294 memset(origdest, 0xBB, nBuf);
295 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
297 int nCopied;
298 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
299 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
300 memset(dest, 0xBB, nBuf);
301 *(WORD *) dest = gl[i].buffer_len;
303 /* EM_GETLINE appends a "\r\0" to the end of the line
304 * nCopied counts up to and including the '\r' */
305 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
306 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
307 expected_nCopied);
308 /* two special cases since a parameter is passed via dest */
309 if (gl[i].buffer_len == 0)
310 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
311 "buffer_len=0\n");
312 else if (gl[i].buffer_len == 1)
313 ok(dest[0] == gl[i].text[0] && !dest[1] &&
314 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
315 else
317 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
318 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
319 ok(!strncmp(dest + expected_bytes_written, origdest
320 + expected_bytes_written, nBuf - expected_bytes_written),
321 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
325 DestroyWindow(hwndRichEdit);
328 static void test_EM_LINELENGTH(void)
330 HWND hwndRichEdit = new_richedit(NULL);
331 const char * text =
332 "richedit1\r"
333 "richedit1\n"
334 "richedit1\r\n"
335 "richedit1";
336 int offset_test[10][2] = {
337 {0, 9},
338 {5, 9},
339 {10, 9},
340 {15, 9},
341 {20, 9},
342 {25, 9},
343 {30, 9},
344 {35, 9},
345 {40, 0},
346 {45, 0},
348 int i;
349 LRESULT result;
351 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
353 for (i = 0; i < 10; i++) {
354 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
355 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
356 offset_test[i][0], result, offset_test[i][1]);
359 DestroyWindow(hwndRichEdit);
362 static int get_scroll_pos_y(HWND hwnd)
364 POINT p = {-1, -1};
365 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
366 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
367 return p.y;
370 static void move_cursor(HWND hwnd, long charindex)
372 CHARRANGE cr;
373 cr.cpMax = charindex;
374 cr.cpMin = charindex;
375 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
378 static void line_scroll(HWND hwnd, int amount)
380 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
383 static void test_EM_SCROLLCARET(void)
385 int prevY, curY;
386 HWND hwndRichEdit = new_richedit(NULL);
387 const char text[] = "aa\n"
388 "this is a long line of text that should be longer than the "
389 "control's width\n"
390 "cc\n"
391 "dd\n"
392 "ee\n"
393 "ff\n"
394 "gg\n"
395 "hh\n";
397 /* Can't verify this */
398 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
400 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
402 /* Caret above visible window */
403 line_scroll(hwndRichEdit, 3);
404 prevY = get_scroll_pos_y(hwndRichEdit);
405 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
406 curY = get_scroll_pos_y(hwndRichEdit);
407 ok(prevY != curY, "%d == %d\n", prevY, curY);
409 /* Caret below visible window */
410 move_cursor(hwndRichEdit, sizeof(text) - 1);
411 line_scroll(hwndRichEdit, -3);
412 prevY = get_scroll_pos_y(hwndRichEdit);
413 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
414 curY = get_scroll_pos_y(hwndRichEdit);
415 ok(prevY != curY, "%d == %d\n", prevY, curY);
417 /* Caret in visible window */
418 move_cursor(hwndRichEdit, sizeof(text) - 2);
419 prevY = get_scroll_pos_y(hwndRichEdit);
420 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
421 curY = get_scroll_pos_y(hwndRichEdit);
422 ok(prevY == curY, "%d != %d\n", prevY, curY);
424 /* Caret still in visible window */
425 line_scroll(hwndRichEdit, -1);
426 prevY = get_scroll_pos_y(hwndRichEdit);
427 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
428 curY = get_scroll_pos_y(hwndRichEdit);
429 ok(prevY == curY, "%d != %d\n", prevY, curY);
431 DestroyWindow(hwndRichEdit);
434 static void test_EM_POSFROMCHAR(void)
436 HWND hwndRichEdit = new_richedit(NULL);
437 int i;
438 LRESULT result;
439 unsigned int height = 0;
440 int xpos = 0;
441 static const char text[] = "aa\n"
442 "this is a long line of text that should be longer than the "
443 "control's width\n"
444 "cc\n"
445 "dd\n"
446 "ee\n"
447 "ff\n"
448 "gg\n"
449 "hh\n";
451 /* Fill the control to lines to ensure that most of them are offscreen */
452 for (i = 0; i < 50; i++)
454 /* Do not modify the string; it is exactly 16 characters long. */
455 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
456 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
460 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
461 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
462 Richedit 3.0 accepts either of the above API conventions.
465 /* Testing Richedit 2.0 API format */
467 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
468 Since all lines are identical and drawn with the same font,
469 they should have the same height... right?
471 for (i = 0; i < 50; i++)
473 /* All the lines are 16 characters long */
474 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
475 if (i == 0)
477 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
478 todo_wine {
479 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
481 xpos = LOWORD(result);
483 else if (i == 1)
485 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
486 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
487 height = HIWORD(result);
489 else
491 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
492 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
496 /* Testing position at end of text */
497 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
498 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
499 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
501 /* Testing position way past end of text */
502 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
503 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
504 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
506 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
507 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
508 for (i = 0; i < 50; i++)
510 /* All the lines are 16 characters long */
511 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
512 ok((signed short)(HIWORD(result)) == (i - 1) * height,
513 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
514 (signed short)(HIWORD(result)), (i - 1) * height);
515 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
518 /* Testing position at end of text */
519 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
520 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
521 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
523 /* Testing position way past end of text */
524 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
525 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
526 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
528 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
529 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
530 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
532 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
533 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
534 todo_wine {
535 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
537 xpos = LOWORD(result);
539 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
540 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
541 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
542 todo_wine {
543 /* Fails on builtin because horizontal scrollbar is not being shown */
544 ok((signed short)(LOWORD(result)) < xpos,
545 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
546 (signed short)(LOWORD(result)), xpos);
548 DestroyWindow(hwndRichEdit);
551 static void test_EM_SETCHARFORMAT(void)
553 HWND hwndRichEdit = new_richedit(NULL);
554 CHARFORMAT2 cf2;
555 int rc = 0;
556 int tested_effects[] = {
557 CFE_BOLD,
558 CFE_ITALIC,
559 CFE_UNDERLINE,
560 CFE_STRIKEOUT,
561 CFE_PROTECTED,
562 CFE_LINK,
563 CFE_SUBSCRIPT,
564 CFE_SUPERSCRIPT,
567 int i;
568 CHARRANGE cr;
570 /* Invalid flags, CHARFORMAT2 structure blanked out */
571 memset(&cf2, 0, sizeof(cf2));
572 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
573 (LPARAM) &cf2);
574 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
576 /* A valid flag, CHARFORMAT2 structure blanked out */
577 memset(&cf2, 0, sizeof(cf2));
578 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
579 (LPARAM) &cf2);
580 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
582 /* A valid flag, CHARFORMAT2 structure blanked out */
583 memset(&cf2, 0, sizeof(cf2));
584 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
585 (LPARAM) &cf2);
586 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
588 /* A valid flag, CHARFORMAT2 structure blanked out */
589 memset(&cf2, 0, sizeof(cf2));
590 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
591 (LPARAM) &cf2);
592 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
594 /* A valid flag, CHARFORMAT2 structure blanked out */
595 memset(&cf2, 0, sizeof(cf2));
596 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
597 (LPARAM) &cf2);
598 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
600 /* Invalid flags, CHARFORMAT2 structure minimally filled */
601 memset(&cf2, 0, sizeof(cf2));
602 cf2.cbSize = sizeof(CHARFORMAT2);
603 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
604 (LPARAM) &cf2);
605 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
606 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
607 ok(rc == FALSE, "Should not be able to undo here.\n");
608 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
610 /* A valid flag, CHARFORMAT2 structure minimally filled */
611 memset(&cf2, 0, sizeof(cf2));
612 cf2.cbSize = sizeof(CHARFORMAT2);
613 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
614 (LPARAM) &cf2);
615 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
616 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
617 todo_wine ok(rc == FALSE, "Should not be able to undo here.\n");
618 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
620 /* A valid flag, CHARFORMAT2 structure minimally filled */
621 memset(&cf2, 0, sizeof(cf2));
622 cf2.cbSize = sizeof(CHARFORMAT2);
623 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
624 (LPARAM) &cf2);
625 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
626 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
627 ok(rc == FALSE, "Should not be able to undo here.\n");
628 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
630 /* A valid flag, CHARFORMAT2 structure minimally filled */
631 memset(&cf2, 0, sizeof(cf2));
632 cf2.cbSize = sizeof(CHARFORMAT2);
633 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
634 (LPARAM) &cf2);
635 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
636 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
637 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
638 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
640 /* A valid flag, CHARFORMAT2 structure minimally filled */
641 memset(&cf2, 0, sizeof(cf2));
642 cf2.cbSize = sizeof(CHARFORMAT2);
643 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
644 (LPARAM) &cf2);
645 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
646 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
647 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
648 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
650 cf2.cbSize = sizeof(CHARFORMAT2);
651 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
652 (LPARAM) &cf2);
654 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
655 cf2.cbSize = sizeof(CHARFORMAT2);
656 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
657 (LPARAM) &cf2);
658 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
659 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
661 /* wParam==0 is default char format, does not set modify */
662 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
663 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
664 ok(rc == 0, "Text marked as modified, expected not modified!\n");
665 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
666 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
667 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
668 ok(rc == 0, "Text marked as modified, expected not modified!\n");
670 /* wParam==SCF_SELECTION sets modify if nonempty selection */
671 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
672 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
673 ok(rc == 0, "Text marked as modified, expected not modified!\n");
674 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
675 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
676 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
677 ok(rc == 0, "Text marked as modified, expected not modified!\n");
679 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
680 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
681 ok(rc == 0, "Text marked as modified, expected not modified!\n");
682 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
683 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
684 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
685 ok(rc == 0, "Text marked as modified, expected not modified!\n");
686 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
687 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
688 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
689 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
690 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
692 /* wParam==SCF_ALL sets modify regardless of whether text is present */
693 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
694 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
695 ok(rc == 0, "Text marked as modified, expected not modified!\n");
696 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
697 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
698 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
699 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
701 DestroyWindow(hwndRichEdit);
703 /* EM_GETCHARFORMAT tests */
704 for (i = 0; tested_effects[i]; i++)
706 hwndRichEdit = new_richedit(NULL);
707 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
709 /* Need to set a TrueType font to get consistent CFM_BOLD results */
710 memset(&cf2, 0, sizeof(CHARFORMAT2));
711 cf2.cbSize = sizeof(CHARFORMAT2);
712 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
713 cf2.dwEffects = 0;
714 strcpy(cf2.szFaceName, "Courier New");
715 cf2.wWeight = FW_DONTCARE;
716 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
718 memset(&cf2, 0, sizeof(CHARFORMAT2));
719 cf2.cbSize = sizeof(CHARFORMAT2);
720 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
721 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
722 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
723 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
725 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
726 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
727 ok((cf2.dwEffects & tested_effects[i]) == 0,
728 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
730 memset(&cf2, 0, sizeof(CHARFORMAT2));
731 cf2.cbSize = sizeof(CHARFORMAT2);
732 cf2.dwMask = tested_effects[i];
733 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
734 cf2.dwMask = CFM_SUPERSCRIPT;
735 cf2.dwEffects = tested_effects[i];
736 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
737 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
739 memset(&cf2, 0, sizeof(CHARFORMAT2));
740 cf2.cbSize = sizeof(CHARFORMAT2);
741 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
742 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
743 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
744 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
746 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
747 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
748 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
749 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
751 memset(&cf2, 0, sizeof(CHARFORMAT2));
752 cf2.cbSize = sizeof(CHARFORMAT2);
753 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
754 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
755 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
756 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
758 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
759 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
760 ok((cf2.dwEffects & tested_effects[i]) == 0,
761 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
763 memset(&cf2, 0, sizeof(CHARFORMAT2));
764 cf2.cbSize = sizeof(CHARFORMAT2);
765 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
766 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
767 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
768 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
770 (cf2.dwMask & tested_effects[i]) == 0),
771 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
773 DestroyWindow(hwndRichEdit);
776 for (i = 0; tested_effects[i]; i++)
778 hwndRichEdit = new_richedit(NULL);
779 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
781 /* Need to set a TrueType font to get consistent CFM_BOLD results */
782 memset(&cf2, 0, sizeof(CHARFORMAT2));
783 cf2.cbSize = sizeof(CHARFORMAT2);
784 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
785 cf2.dwEffects = 0;
786 strcpy(cf2.szFaceName, "Courier New");
787 cf2.wWeight = FW_DONTCARE;
788 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
790 memset(&cf2, 0, sizeof(CHARFORMAT2));
791 cf2.cbSize = sizeof(CHARFORMAT2);
792 cf2.dwMask = tested_effects[i];
793 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
794 cf2.dwMask = CFM_SUPERSCRIPT;
795 cf2.dwEffects = tested_effects[i];
796 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
797 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
799 memset(&cf2, 0, sizeof(CHARFORMAT2));
800 cf2.cbSize = sizeof(CHARFORMAT2);
801 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
802 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
803 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
804 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
806 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
807 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
808 ok((cf2.dwEffects & tested_effects[i]) == 0,
809 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
811 memset(&cf2, 0, sizeof(CHARFORMAT2));
812 cf2.cbSize = sizeof(CHARFORMAT2);
813 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
814 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
815 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
816 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
818 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
819 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
820 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
821 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
823 memset(&cf2, 0, sizeof(CHARFORMAT2));
824 cf2.cbSize = sizeof(CHARFORMAT2);
825 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
826 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
827 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
828 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
830 (cf2.dwMask & tested_effects[i]) == 0),
831 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
832 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
833 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
835 DestroyWindow(hwndRichEdit);
838 /* Effects applied on an empty selection should take effect when selection is
839 replaced with text */
840 hwndRichEdit = new_richedit(NULL);
841 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
842 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
844 memset(&cf2, 0, sizeof(CHARFORMAT2));
845 cf2.cbSize = sizeof(CHARFORMAT2);
846 cf2.dwMask = CFM_BOLD;
847 cf2.dwEffects = CFE_BOLD;
848 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
850 /* Selection is now nonempty */
851 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
853 memset(&cf2, 0, sizeof(CHARFORMAT2));
854 cf2.cbSize = sizeof(CHARFORMAT2);
855 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
856 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
858 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
859 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
860 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
861 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
864 /* Set two effects on an empty selection */
865 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
866 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
868 memset(&cf2, 0, sizeof(CHARFORMAT2));
869 cf2.cbSize = sizeof(CHARFORMAT2);
870 cf2.dwMask = CFM_BOLD;
871 cf2.dwEffects = CFE_BOLD;
872 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
873 cf2.dwMask = CFM_ITALIC;
874 cf2.dwEffects = CFE_ITALIC;
875 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
877 /* Selection is now nonempty */
878 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
880 memset(&cf2, 0, sizeof(CHARFORMAT2));
881 cf2.cbSize = sizeof(CHARFORMAT2);
882 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
883 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
885 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
886 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
887 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
888 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
890 /* Setting the (empty) selection to exactly the same place as before should
891 NOT clear the insertion style! */
892 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
893 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
895 memset(&cf2, 0, sizeof(CHARFORMAT2));
896 cf2.cbSize = sizeof(CHARFORMAT2);
897 cf2.dwMask = CFM_BOLD;
898 cf2.dwEffects = CFE_BOLD;
899 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
901 /* Empty selection in same place, insert style should NOT be forgotten here. */
902 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
904 /* Selection is now nonempty */
905 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
907 memset(&cf2, 0, sizeof(CHARFORMAT2));
908 cf2.cbSize = sizeof(CHARFORMAT2);
909 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
910 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
912 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
913 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
914 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
915 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
917 /* Ditto with EM_EXSETSEL */
918 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
919 cr.cpMin = 2; cr.cpMax = 2;
920 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
922 memset(&cf2, 0, sizeof(CHARFORMAT2));
923 cf2.cbSize = sizeof(CHARFORMAT2);
924 cf2.dwMask = CFM_BOLD;
925 cf2.dwEffects = CFE_BOLD;
926 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
928 /* Empty selection in same place, insert style should NOT be forgotten here. */
929 cr.cpMin = 2; cr.cpMax = 2;
930 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
932 /* Selection is now nonempty */
933 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
935 memset(&cf2, 0, sizeof(CHARFORMAT2));
936 cf2.cbSize = sizeof(CHARFORMAT2);
937 cr.cpMin = 2; cr.cpMax = 6;
938 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
939 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
941 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
942 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
943 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
944 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
946 DestroyWindow(hwndRichEdit);
949 static void test_EM_SETTEXTMODE(void)
951 HWND hwndRichEdit = new_richedit(NULL);
952 CHARFORMAT2 cf2, cf2test;
953 CHARRANGE cr;
954 int rc = 0;
956 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
957 /*Insert text into the control*/
959 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
961 /*Attempt to change the control to plain text mode*/
962 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
963 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
965 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
966 If rich text is pasted, it should have the same formatting as the rest
967 of the text in the control*/
969 /*Italicize the text
970 *NOTE: If the default text was already italicized, the test will simply
971 reverse; in other words, it will copy a regular "wine" into a plain
972 text window that uses an italicized format*/
973 cf2.cbSize = sizeof(CHARFORMAT2);
974 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
975 (LPARAM) &cf2);
977 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
978 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
980 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
981 ok(rc == 0, "Text marked as modified, expected not modified!\n");
983 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
984 however, SCF_ALL has been implemented*/
985 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
986 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
988 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
989 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
991 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
993 /*Select the string "wine"*/
994 cr.cpMin = 0;
995 cr.cpMax = 4;
996 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
998 /*Copy the italicized "wine" to the clipboard*/
999 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1001 /*Reset the formatting to default*/
1002 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1003 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1004 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1006 /*Clear the text in the control*/
1007 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1009 /*Switch to Plain Text Mode*/
1010 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1011 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1013 /*Input "wine" again in normal format*/
1014 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1016 /*Paste the italicized "wine" into the control*/
1017 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1019 /*Select a character from the first "wine" string*/
1020 cr.cpMin = 2;
1021 cr.cpMax = 3;
1022 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1024 /*Retrieve its formatting*/
1025 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1026 (LPARAM) &cf2);
1028 /*Select a character from the second "wine" string*/
1029 cr.cpMin = 5;
1030 cr.cpMax = 6;
1031 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1033 /*Retrieve its formatting*/
1034 cf2test.cbSize = sizeof(CHARFORMAT2);
1035 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1036 (LPARAM) &cf2test);
1038 /*Compare the two formattings*/
1039 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1040 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1041 cf2.dwEffects, cf2test.dwEffects);
1042 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1043 printing "wine" in the current format(normal)
1044 pasting "wine" from the clipboard(italicized)
1045 comparing the two formats(should differ)*/
1047 /*Attempt to switch with text in control*/
1048 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1049 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1051 /*Clear control*/
1052 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1054 /*Switch into Rich Text mode*/
1055 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1056 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1058 /*Print "wine" in normal formatting into the control*/
1059 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1061 /*Paste italicized "wine" into the control*/
1062 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1064 /*Select text from the first "wine" string*/
1065 cr.cpMin = 1;
1066 cr.cpMax = 3;
1067 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1069 /*Retrieve its formatting*/
1070 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1071 (LPARAM) &cf2);
1073 /*Select text from the second "wine" string*/
1074 cr.cpMin = 6;
1075 cr.cpMax = 7;
1076 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1078 /*Retrieve its formatting*/
1079 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1080 (LPARAM) &cf2test);
1082 /*Test that the two formattings are not the same*/
1083 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1084 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1085 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1087 DestroyWindow(hwndRichEdit);
1090 static void test_TM_PLAINTEXT(void)
1092 /*Tests plain text properties*/
1094 HWND hwndRichEdit = new_richedit(NULL);
1095 CHARFORMAT2 cf2, cf2test;
1096 CHARRANGE cr;
1097 int rc = 0;
1099 /*Switch to plain text mode*/
1101 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1102 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1104 /*Fill control with text*/
1106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1108 /*Select some text and bold it*/
1110 cr.cpMin = 10;
1111 cr.cpMax = 20;
1112 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1113 cf2.cbSize = sizeof(CHARFORMAT2);
1114 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1115 (LPARAM) &cf2);
1117 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1118 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1120 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1121 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1123 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1124 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1126 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1127 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1129 /*Get the formatting of those characters*/
1131 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1133 /*Get the formatting of some other characters*/
1134 cf2test.cbSize = sizeof(CHARFORMAT2);
1135 cr.cpMin = 21;
1136 cr.cpMax = 30;
1137 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1138 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1140 /*Test that they are the same as plain text allows only one formatting*/
1142 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1143 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1144 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1146 /*Fill the control with a "wine" string, which when inserted will be bold*/
1148 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1150 /*Copy the bolded "wine" string*/
1152 cr.cpMin = 0;
1153 cr.cpMax = 4;
1154 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1155 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1157 /*Swap back to rich text*/
1159 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1160 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1162 /*Set the default formatting to bold italics*/
1164 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1165 cf2.dwMask |= CFM_ITALIC;
1166 cf2.dwEffects ^= CFE_ITALIC;
1167 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1168 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1170 /*Set the text in the control to "wine", which will be bold and italicized*/
1172 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1174 /*Paste the plain text "wine" string, which should take the insert
1175 formatting, which at the moment is bold italics*/
1177 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1179 /*Select the first "wine" string and retrieve its formatting*/
1181 cr.cpMin = 1;
1182 cr.cpMax = 3;
1183 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1184 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1186 /*Select the second "wine" string and retrieve its formatting*/
1188 cr.cpMin = 5;
1189 cr.cpMax = 7;
1190 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1191 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1193 /*Compare the two formattings. They should be the same.*/
1195 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1196 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1197 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1198 DestroyWindow(hwndRichEdit);
1201 static void test_WM_GETTEXT(void)
1203 HWND hwndRichEdit = new_richedit(NULL);
1204 static const char text[] = "Hello. My name is RichEdit!";
1205 static const char text2[] = "Hello. My name is RichEdit!\r";
1206 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1207 char buffer[1024] = {0};
1208 int result;
1210 /* Baseline test with normal-sized buffer */
1211 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1212 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1213 ok(result == lstrlen(buffer),
1214 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1215 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1216 result = strcmp(buffer,text);
1217 ok(result == 0,
1218 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1220 /* Test for returned value of WM_GETTEXTLENGTH */
1221 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1222 ok(result == lstrlen(text),
1223 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1224 result, lstrlen(text));
1226 /* Test for behavior in overflow case */
1227 memset(buffer, 0, 1024);
1228 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1229 ok(result == 0 ||
1230 result == lstrlenA(text) - 1, /* XP, win2k3 */
1231 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1232 result = strcmp(buffer,text);
1233 if (result)
1234 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1235 ok(result == 0,
1236 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1238 /* Baseline test with normal-sized buffer and carriage return */
1239 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1240 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1241 ok(result == lstrlen(buffer),
1242 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1243 result = strcmp(buffer,text2_after);
1244 ok(result == 0,
1245 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1247 /* Test for returned value of WM_GETTEXTLENGTH */
1248 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1249 ok(result == lstrlen(text2_after),
1250 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1251 result, lstrlen(text2_after));
1253 /* Test for behavior of CRLF conversion in case of overflow */
1254 memset(buffer, 0, 1024);
1255 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1256 ok(result == 0 ||
1257 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1258 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1259 result = strcmp(buffer,text2);
1260 if (result)
1261 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1262 ok(result == 0,
1263 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1265 DestroyWindow(hwndRichEdit);
1268 static void test_EM_GETTEXTRANGE(void)
1270 HWND hwndRichEdit = new_richedit(NULL);
1271 const char * text1 = "foo bar\r\nfoo bar";
1272 const char * text2 = "foo bar\rfoo bar";
1273 const char * expect = "bar\rfoo";
1274 char buffer[1024] = {0};
1275 LRESULT result;
1276 TEXTRANGEA textRange;
1278 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1280 textRange.lpstrText = buffer;
1281 textRange.chrg.cpMin = 4;
1282 textRange.chrg.cpMax = 11;
1283 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1284 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1285 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1287 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1289 textRange.lpstrText = buffer;
1290 textRange.chrg.cpMin = 4;
1291 textRange.chrg.cpMax = 11;
1292 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1293 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1294 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1296 DestroyWindow(hwndRichEdit);
1299 static void test_EM_GETSELTEXT(void)
1301 HWND hwndRichEdit = new_richedit(NULL);
1302 const char * text1 = "foo bar\r\nfoo bar";
1303 const char * text2 = "foo bar\rfoo bar";
1304 const char * expect = "bar\rfoo";
1305 char buffer[1024] = {0};
1306 LRESULT result;
1308 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1310 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1311 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1312 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1313 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1315 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1317 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1318 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1319 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1320 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1322 DestroyWindow(hwndRichEdit);
1325 /* FIXME: need to test unimplemented options and robustly test wparam */
1326 static void test_EM_SETOPTIONS(void)
1328 HWND hwndRichEdit = new_richedit(NULL);
1329 static const char text[] = "Hello. My name is RichEdit!";
1330 char buffer[1024] = {0};
1332 /* NEGATIVE TESTING - NO OPTIONS SET */
1333 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1334 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1336 /* testing no readonly by sending 'a' to the control*/
1337 SetFocus(hwndRichEdit);
1338 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1339 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1340 ok(buffer[0]=='a',
1341 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1342 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1344 /* READONLY - sending 'a' to the control */
1345 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1346 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1347 SetFocus(hwndRichEdit);
1348 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1349 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1350 ok(buffer[0]==text[0],
1351 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1353 DestroyWindow(hwndRichEdit);
1356 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1358 CHARFORMAT2W text_format;
1359 text_format.cbSize = sizeof(text_format);
1360 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1361 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1362 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1365 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1367 int link_present = 0;
1369 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1370 if (is_url)
1371 { /* control text is url; should get CFE_LINK */
1372 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1374 else
1376 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1380 static HWND new_static_wnd(HWND parent) {
1381 return new_window("Static", 0, parent);
1384 static void test_EM_AUTOURLDETECT(void)
1386 struct urls_s {
1387 const char *text;
1388 int is_url;
1389 } urls[12] = {
1390 {"winehq.org", 0},
1391 {"http://www.winehq.org", 1},
1392 {"http//winehq.org", 0},
1393 {"ww.winehq.org", 0},
1394 {"www.winehq.org", 1},
1395 {"ftp://192.168.1.1", 1},
1396 {"ftp//192.168.1.1", 0},
1397 {"mailto:your@email.com", 1},
1398 {"prospero:prosperoserver", 1},
1399 {"telnet:test", 1},
1400 {"news:newserver", 1},
1401 {"wais:waisserver", 1}
1404 int i, j;
1405 int urlRet=-1;
1406 HWND hwndRichEdit, parent;
1408 /* All of the following should cause the URL to be detected */
1409 const char * templates_delim[] = {
1410 "This is some text with X on it",
1411 "This is some text with (X) on it",
1412 "This is some text with X\r on it",
1413 "This is some text with ---X--- on it",
1414 "This is some text with \"X\" on it",
1415 "This is some text with 'X' on it",
1416 "This is some text with 'X' on it",
1417 "This is some text with :X: on it",
1419 "This text ends with X",
1421 "This is some text with X) on it",
1422 "This is some text with X--- on it",
1423 "This is some text with X\" on it",
1424 "This is some text with X' on it",
1425 "This is some text with X: on it",
1427 "This is some text with (X on it",
1428 "This is some text with \rX on it",
1429 "This is some text with ---X on it",
1430 "This is some text with \"X on it",
1431 "This is some text with 'X on it",
1432 "This is some text with :X on it",
1434 /* None of these should cause the URL to be detected */
1435 const char * templates_non_delim[] = {
1436 "This is some text with |X| on it",
1437 "This is some text with *X* on it",
1438 "This is some text with /X/ on it",
1439 "This is some text with +X+ on it",
1440 "This is some text with %X% on it",
1441 "This is some text with #X# on it",
1442 "This is some text with @X@ on it",
1443 "This is some text with \\X\\ on it",
1444 "This is some text with |X on it",
1445 "This is some text with *X on it",
1446 "This is some text with /X on it",
1447 "This is some text with +X on it",
1448 "This is some text with %X on it",
1449 "This is some text with #X on it",
1450 "This is some text with @X on it",
1451 "This is some text with \\X on it",
1453 /* All of these cause the URL detection to be extended by one more byte,
1454 thus demonstrating that the tested character is considered as part
1455 of the URL. */
1456 const char * templates_xten_delim[] = {
1457 "This is some text with X| on it",
1458 "This is some text with X* on it",
1459 "This is some text with X/ on it",
1460 "This is some text with X+ on it",
1461 "This is some text with X% on it",
1462 "This is some text with X# on it",
1463 "This is some text with X@ on it",
1464 "This is some text with X\\ on it",
1466 char buffer[1024];
1468 parent = new_static_wnd(NULL);
1469 hwndRichEdit = new_richedit(parent);
1470 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1471 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1472 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1473 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1474 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1475 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1476 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1477 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1478 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1479 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1480 /* for each url, check the text to see if CFE_LINK effect is present */
1481 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1483 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1484 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1485 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1487 /* Link detection should happen immediately upon WM_SETTEXT */
1488 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1489 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1490 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1492 DestroyWindow(hwndRichEdit);
1494 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1495 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1496 hwndRichEdit = new_richedit(parent);
1498 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1499 char * at_pos;
1500 int at_offset;
1501 int end_offset;
1503 at_pos = strchr(templates_delim[j], 'X');
1504 at_offset = at_pos - templates_delim[j];
1505 strncpy(buffer, templates_delim[j], at_offset);
1506 buffer[at_offset] = '\0';
1507 strcat(buffer, urls[i].text);
1508 strcat(buffer, templates_delim[j] + at_offset + 1);
1509 end_offset = at_offset + strlen(urls[i].text);
1511 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1512 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1514 /* This assumes no templates start with the URL itself, and that they
1515 have at least two characters before the URL text */
1516 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1517 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1518 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1519 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1520 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1521 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1523 if (urls[i].is_url)
1525 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1526 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1527 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1528 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1530 else
1532 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1533 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1534 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1535 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1537 if (buffer[end_offset] != '\0')
1539 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1540 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1541 if (buffer[end_offset +1] != '\0')
1543 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1544 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1549 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1550 char * at_pos;
1551 int at_offset;
1552 int end_offset;
1554 at_pos = strchr(templates_non_delim[j], 'X');
1555 at_offset = at_pos - templates_non_delim[j];
1556 strncpy(buffer, templates_non_delim[j], at_offset);
1557 buffer[at_offset] = '\0';
1558 strcat(buffer, urls[i].text);
1559 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1560 end_offset = at_offset + strlen(urls[i].text);
1562 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1563 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1565 /* This assumes no templates start with the URL itself, and that they
1566 have at least two characters before the URL text */
1567 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1568 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1569 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1570 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1571 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1572 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1574 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1575 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1576 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1577 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1578 if (buffer[end_offset] != '\0')
1580 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1581 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1582 if (buffer[end_offset +1] != '\0')
1584 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1585 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1590 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1591 char * at_pos;
1592 int at_offset;
1593 int end_offset;
1595 at_pos = strchr(templates_xten_delim[j], 'X');
1596 at_offset = at_pos - templates_xten_delim[j];
1597 strncpy(buffer, templates_xten_delim[j], at_offset);
1598 buffer[at_offset] = '\0';
1599 strcat(buffer, urls[i].text);
1600 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1601 end_offset = at_offset + strlen(urls[i].text);
1603 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1604 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1606 /* This assumes no templates start with the URL itself, and that they
1607 have at least two characters before the URL text */
1608 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1609 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1610 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1611 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1612 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1613 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1615 if (urls[i].is_url)
1617 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1618 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1619 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1620 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1621 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1622 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1624 else
1626 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1627 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1628 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1629 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1630 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1631 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1633 if (buffer[end_offset +1] != '\0')
1635 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1636 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1637 if (buffer[end_offset +2] != '\0')
1639 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1640 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1645 DestroyWindow(hwndRichEdit);
1646 hwndRichEdit = NULL;
1649 /* Test detection of URLs within normal text - WM_CHAR case. */
1650 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1651 hwndRichEdit = new_richedit(parent);
1653 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1654 char * at_pos;
1655 int at_offset;
1656 int end_offset;
1657 int u, v;
1659 at_pos = strchr(templates_delim[j], 'X');
1660 at_offset = at_pos - templates_delim[j];
1661 end_offset = at_offset + strlen(urls[i].text);
1663 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1664 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1665 for (u = 0; templates_delim[j][u]; u++) {
1666 if (templates_delim[j][u] == '\r') {
1667 simulate_typing_characters(hwndRichEdit, "\r");
1668 } else if (templates_delim[j][u] != 'X') {
1669 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1670 } else {
1671 for (v = 0; urls[i].text[v]; v++) {
1672 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1676 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1677 trace("Using template: %s\n", templates_delim[j]);
1679 /* This assumes no templates start with the URL itself, and that they
1680 have at least two characters before the URL text */
1681 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1682 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1683 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1684 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1685 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1686 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1688 if (urls[i].is_url)
1690 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1691 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1692 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1693 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1695 else
1697 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1698 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1699 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1700 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1702 if (buffer[end_offset] != '\0')
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);
1706 if (buffer[end_offset +1] != '\0')
1708 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1713 /* The following will insert a paragraph break after the first character
1714 of the URL candidate, thus breaking the URL. It is expected that the
1715 CFE_LINK attribute should break across both pieces of the URL */
1716 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1717 simulate_typing_characters(hwndRichEdit, "\r");
1718 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1720 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1721 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1722 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1723 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1724 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1725 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1727 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1728 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1729 /* end_offset moved because of paragraph break */
1730 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1731 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1732 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1733 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1735 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1736 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1737 if (buffer[end_offset +2] != '\0')
1739 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1740 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1744 /* The following will remove the just-inserted paragraph break, thus
1745 restoring the URL */
1746 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1747 simulate_typing_characters(hwndRichEdit, "\b");
1748 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1750 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1751 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1752 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1753 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1754 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1755 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1757 if (urls[i].is_url)
1759 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1760 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1761 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1762 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1764 else
1766 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1767 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1768 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1769 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1771 if (buffer[end_offset] != '\0')
1773 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1774 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1775 if (buffer[end_offset +1] != '\0')
1777 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1778 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1782 DestroyWindow(hwndRichEdit);
1783 hwndRichEdit = NULL;
1786 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1787 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1788 SETTEXTEX st;
1790 hwndRichEdit = new_richedit(parent);
1792 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1793 be detected:
1794 1) Set entire text, a la WM_SETTEXT
1795 2) Set a selection of the text to the URL
1796 3) Set a portion of the text at a time, which eventually results in
1797 an URL
1798 All of them should give equivalent results
1801 /* Set entire text in one go, like WM_SETTEXT */
1802 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1803 char * at_pos;
1804 int at_offset;
1805 int end_offset;
1807 st.codepage = CP_ACP;
1808 st.flags = ST_DEFAULT;
1810 at_pos = strchr(templates_delim[j], 'X');
1811 at_offset = at_pos - templates_delim[j];
1812 strncpy(buffer, templates_delim[j], at_offset);
1813 buffer[at_offset] = '\0';
1814 strcat(buffer, urls[i].text);
1815 strcat(buffer, templates_delim[j] + at_offset + 1);
1816 end_offset = at_offset + strlen(urls[i].text);
1818 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1819 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1821 /* This assumes no templates start with the URL itself, and that they
1822 have at least two characters before the URL text */
1823 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1824 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1825 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1826 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1827 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1828 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1830 if (urls[i].is_url)
1832 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1833 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1834 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1835 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1837 else
1839 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1840 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1841 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1842 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1844 if (buffer[end_offset] != '\0')
1846 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1847 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1848 if (buffer[end_offset +1] != '\0')
1850 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1851 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1856 /* Set selection with X to the URL */
1857 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1858 char * at_pos;
1859 int at_offset;
1860 int end_offset;
1862 at_pos = strchr(templates_delim[j], 'X');
1863 at_offset = at_pos - templates_delim[j];
1864 end_offset = at_offset + strlen(urls[i].text);
1866 st.codepage = CP_ACP;
1867 st.flags = ST_DEFAULT;
1868 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1869 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1870 st.flags = ST_SELECTION;
1871 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1872 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1873 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1875 /* This assumes no templates start with the URL itself, and that they
1876 have at least two characters before the URL text */
1877 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1878 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1881 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1882 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1884 if (urls[i].is_url)
1886 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1887 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1888 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1889 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1891 else
1893 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1894 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1895 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1896 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1898 if (buffer[end_offset] != '\0')
1900 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1901 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1902 if (buffer[end_offset +1] != '\0')
1904 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1905 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1910 /* Set selection with X to the first character of the URL, then the rest */
1911 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1912 char * at_pos;
1913 int at_offset;
1914 int end_offset;
1916 at_pos = strchr(templates_delim[j], 'X');
1917 at_offset = at_pos - templates_delim[j];
1918 end_offset = at_offset + strlen(urls[i].text);
1920 strcpy(buffer, "YY");
1921 buffer[0] = urls[i].text[0];
1923 st.codepage = CP_ACP;
1924 st.flags = ST_DEFAULT;
1925 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1926 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1927 st.flags = ST_SELECTION;
1928 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1929 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1930 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1931 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1932 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1934 /* This assumes no templates start with the URL itself, and that they
1935 have at least two characters before the URL text */
1936 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1937 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1938 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1939 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1940 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1941 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1943 if (urls[i].is_url)
1945 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1946 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1947 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1948 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1950 else
1952 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1953 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1954 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1955 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1957 if (buffer[end_offset] != '\0')
1959 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1960 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1961 if (buffer[end_offset +1] != '\0')
1963 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1964 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1969 DestroyWindow(hwndRichEdit);
1970 hwndRichEdit = NULL;
1973 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
1974 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1975 hwndRichEdit = new_richedit(parent);
1977 /* Set selection with X to the URL */
1978 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1979 char * at_pos;
1980 int at_offset;
1981 int end_offset;
1983 at_pos = strchr(templates_delim[j], 'X');
1984 at_offset = at_pos - templates_delim[j];
1985 end_offset = at_offset + strlen(urls[i].text);
1987 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1988 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
1989 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1990 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
1991 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1993 /* This assumes no templates start with the URL itself, and that they
1994 have at least two characters before the URL text */
1995 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1996 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1997 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1998 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1999 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2000 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2002 if (urls[i].is_url)
2004 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2005 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2006 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2007 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2009 else
2011 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2012 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2013 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2014 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2016 if (buffer[end_offset] != '\0')
2018 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2019 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2020 if (buffer[end_offset +1] != '\0')
2022 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2023 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2028 /* Set selection with X to the first character of the URL, then the rest */
2029 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2030 char * at_pos;
2031 int at_offset;
2032 int end_offset;
2034 at_pos = strchr(templates_delim[j], 'X');
2035 at_offset = at_pos - templates_delim[j];
2036 end_offset = at_offset + strlen(urls[i].text);
2038 strcpy(buffer, "YY");
2039 buffer[0] = urls[i].text[0];
2041 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2042 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2043 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2044 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2045 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2046 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2047 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2049 /* This assumes no templates start with the URL itself, and that they
2050 have at least two characters before the URL text */
2051 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2052 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2053 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2054 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2058 if (urls[i].is_url)
2060 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2061 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2062 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2063 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2065 else
2067 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2068 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2069 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2070 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2072 if (buffer[end_offset] != '\0')
2074 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2075 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2076 if (buffer[end_offset +1] != '\0')
2078 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2079 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2084 DestroyWindow(hwndRichEdit);
2085 hwndRichEdit = NULL;
2088 DestroyWindow(parent);
2091 static void test_EM_SCROLL(void)
2093 int i, j;
2094 int r; /* return value */
2095 int expr; /* expected return value */
2096 HWND hwndRichEdit = new_richedit(NULL);
2097 int y_before, y_after; /* units of lines of text */
2099 /* test a richedit box containing a single line of text */
2100 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2101 expr = 0x00010000;
2102 for (i = 0; i < 4; i++) {
2103 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2105 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2106 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2107 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2108 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2109 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2110 "(i == %d)\n", y_after, i);
2114 * test a richedit box that will scroll. There are two general
2115 * cases: the case without any long lines and the case with a long
2116 * line.
2118 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2119 if (i == 0)
2120 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2121 else
2122 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2123 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2124 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2125 "LONG LINE \nb\nc\nd\ne");
2126 for (j = 0; j < 12; j++) /* reset scroll position to top */
2127 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2129 /* get first visible line */
2130 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2131 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2133 /* get new current first visible line */
2134 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2136 ok(((r & 0xffffff00) == 0x00010000) &&
2137 ((r & 0x000000ff) != 0x00000000),
2138 "EM_SCROLL page down didn't scroll by a small positive number of "
2139 "lines (r == 0x%08x)\n", r);
2140 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2141 "(line %d scrolled to line %d\n", y_before, y_after);
2143 y_before = y_after;
2145 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2146 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2147 ok(((r & 0xffffff00) == 0x0001ff00),
2148 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2149 "(r == 0x%08x)\n", r);
2150 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2151 "%d scrolled to line %d\n", y_before, y_after);
2153 y_before = y_after;
2155 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2157 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2159 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2160 "(r == 0x%08x)\n", r);
2161 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2162 "1 line (%d scrolled to %d)\n", y_before, y_after);
2164 y_before = y_after;
2166 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2168 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2170 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2171 "(r == 0x%08x)\n", r);
2172 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2173 "line (%d scrolled to %d)\n", y_before, y_after);
2175 y_before = y_after;
2177 r = SendMessage(hwndRichEdit, EM_SCROLL,
2178 SB_LINEUP, 0); /* lineup beyond top */
2180 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2182 ok(r == 0x00010000,
2183 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2184 ok(y_before == y_after,
2185 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2187 y_before = y_after;
2189 r = SendMessage(hwndRichEdit, EM_SCROLL,
2190 SB_PAGEUP, 0);/*page up beyond top */
2192 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2194 ok(r == 0x00010000,
2195 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2196 ok(y_before == y_after,
2197 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2199 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2200 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2201 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2202 r = SendMessage(hwndRichEdit, EM_SCROLL,
2203 SB_PAGEDOWN, 0); /* page down beyond bot */
2204 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2206 ok(r == 0x00010000,
2207 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2208 ok(y_before == y_after,
2209 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2210 y_before, y_after);
2212 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2213 SendMessage(hwndRichEdit, EM_SCROLL,
2214 SB_LINEDOWN, 0); /* line down beyond bot */
2215 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2217 ok(r == 0x00010000,
2218 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2219 ok(y_before == y_after,
2220 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2221 y_before, y_after);
2223 DestroyWindow(hwndRichEdit);
2226 static void test_EM_SETUNDOLIMIT(void)
2228 /* cases we test for:
2229 * default behaviour - limiting at 100 undo's
2230 * undo disabled - setting a limit of 0
2231 * undo limited - undo limit set to some to some number, like 2
2232 * bad input - sending a negative number should default to 100 undo's */
2234 HWND hwndRichEdit = new_richedit(NULL);
2235 CHARRANGE cr;
2236 int i;
2237 int result;
2239 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2240 cr.cpMin = 0;
2241 cr.cpMax = 1;
2242 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2243 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2244 also, multiple pastes don't combine like WM_CHAR would */
2245 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2247 /* first case - check the default */
2248 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2249 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2250 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2251 for (i=0; i<100; i++) /* Undo 100 of them */
2252 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2253 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2254 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
2256 /* second case - cannot undo */
2257 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2258 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
2259 SendMessage(hwndRichEdit,
2260 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
2261 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2262 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
2264 /* third case - set it to an arbitrary number */
2265 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2266 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
2267 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2268 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2269 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2270 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
2271 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
2272 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
2273 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2274 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2275 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
2276 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2277 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2278 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
2280 /* fourth case - setting negative numbers should default to 100 undos */
2281 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2282 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
2283 ok (result == 100,
2284 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
2286 DestroyWindow(hwndRichEdit);
2289 static void test_ES_PASSWORD(void)
2291 /* This isn't hugely testable, so we're just going to run it through its paces */
2293 HWND hwndRichEdit = new_richedit(NULL);
2294 WCHAR result;
2296 /* First, check the default of a regular control */
2297 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2298 ok (result == 0,
2299 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
2301 /* Now, set it to something normal */
2302 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
2303 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2304 ok (result == 120,
2305 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2307 /* Now, set it to something odd */
2308 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
2309 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2310 ok (result == 1234,
2311 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2312 DestroyWindow(hwndRichEdit);
2315 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
2316 LPBYTE pbBuff,
2317 LONG cb,
2318 LONG *pcb)
2320 char** str = (char**)dwCookie;
2321 *pcb = cb;
2322 if (*pcb > 0) {
2323 memcpy(*str, pbBuff, *pcb);
2324 *str += *pcb;
2326 return 0;
2329 static void test_WM_SETTEXT()
2331 HWND hwndRichEdit = new_richedit(NULL);
2332 const char * TestItem1 = "TestSomeText";
2333 const char * TestItem2 = "TestSomeText\r";
2334 const char * TestItem2_after = "TestSomeText\r\n";
2335 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
2336 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
2337 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
2338 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
2339 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
2340 const char * TestItem5_after = "TestSomeText TestSomeText";
2341 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
2342 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
2343 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
2344 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
2346 char buf[1024] = {0};
2347 LRESULT result;
2348 EDITSTREAM es;
2349 char * p;
2351 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
2352 any solitary \r to be converted to \r\n on return. Properly paired
2353 \r\n are not affected. It also shows that the special sequence \r\r\n
2354 gets converted to a single space.
2357 #define TEST_SETTEXT(a, b) \
2358 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
2359 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
2360 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
2361 ok (result == lstrlen(buf), \
2362 "WM_GETTEXT returned %ld instead of expected %u\n", \
2363 result, lstrlen(buf)); \
2364 result = strcmp(b, buf); \
2365 ok(result == 0, \
2366 "WM_SETTEXT round trip: strcmp = %ld\n", result);
2368 TEST_SETTEXT(TestItem1, TestItem1)
2369 TEST_SETTEXT(TestItem2, TestItem2_after)
2370 TEST_SETTEXT(TestItem3, TestItem3_after)
2371 TEST_SETTEXT(TestItem3_after, TestItem3_after)
2372 TEST_SETTEXT(TestItem4, TestItem4_after)
2373 TEST_SETTEXT(TestItem5, TestItem5_after)
2374 TEST_SETTEXT(TestItem6, TestItem6_after)
2375 TEST_SETTEXT(TestItem7, TestItem7_after)
2377 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
2378 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2379 p = buf;
2380 es.dwCookie = (DWORD_PTR)&p;
2381 es.dwError = 0;
2382 es.pfnCallback = test_WM_SETTEXT_esCallback;
2383 memset(buf, 0, sizeof(buf));
2384 SendMessage(hwndRichEdit, EM_STREAMOUT,
2385 (WPARAM)(SF_RTF), (LPARAM)&es);
2386 trace("EM_STREAMOUT produced: \n%s\n", buf);
2387 TEST_SETTEXT(buf, TestItem1)
2389 #undef TEST_SETTEXT
2390 DestroyWindow(hwndRichEdit);
2393 static void test_EM_STREAMOUT(void)
2395 HWND hwndRichEdit = new_richedit(NULL);
2396 int r;
2397 EDITSTREAM es;
2398 char buf[1024] = {0};
2399 char * p;
2401 const char * TestItem1 = "TestSomeText";
2402 const char * TestItem2 = "TestSomeText\r";
2403 const char * TestItem3 = "TestSomeText\r\n";
2405 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2406 p = buf;
2407 es.dwCookie = (DWORD_PTR)&p;
2408 es.dwError = 0;
2409 es.pfnCallback = test_WM_SETTEXT_esCallback;
2410 memset(buf, 0, sizeof(buf));
2411 SendMessage(hwndRichEdit, EM_STREAMOUT,
2412 (WPARAM)(SF_TEXT), (LPARAM)&es);
2413 r = strlen(buf);
2414 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
2415 ok(strcmp(buf, TestItem1) == 0,
2416 "streamed text different, got %s\n", buf);
2418 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
2419 p = buf;
2420 es.dwCookie = (DWORD_PTR)&p;
2421 es.dwError = 0;
2422 es.pfnCallback = test_WM_SETTEXT_esCallback;
2423 memset(buf, 0, sizeof(buf));
2424 SendMessage(hwndRichEdit, EM_STREAMOUT,
2425 (WPARAM)(SF_TEXT), (LPARAM)&es);
2426 r = strlen(buf);
2427 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
2428 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2429 ok(strcmp(buf, TestItem3) == 0,
2430 "streamed text different from, got %s\n", buf);
2431 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
2432 p = buf;
2433 es.dwCookie = (DWORD_PTR)&p;
2434 es.dwError = 0;
2435 es.pfnCallback = test_WM_SETTEXT_esCallback;
2436 memset(buf, 0, sizeof(buf));
2437 SendMessage(hwndRichEdit, EM_STREAMOUT,
2438 (WPARAM)(SF_TEXT), (LPARAM)&es);
2439 r = strlen(buf);
2440 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2441 ok(strcmp(buf, TestItem3) == 0,
2442 "streamed text different, got %s\n", buf);
2444 DestroyWindow(hwndRichEdit);
2447 static void test_EM_SETTEXTEX(void)
2449 HWND hwndRichEdit = new_richedit(NULL);
2450 SETTEXTEX setText;
2451 GETTEXTEX getText;
2452 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2453 'S', 'o', 'm', 'e',
2454 'T', 'e', 'x', 't', 0};
2455 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2456 'S', 'o', 'm', 'e',
2457 'T', 'e', 'x', 't',
2458 '\r', 0};
2459 const char * TestItem2_after = "TestSomeText\r\n";
2460 WCHAR TestItem3[] = {'T', 'e', 's', 't',
2461 'S', 'o', 'm', 'e',
2462 'T', 'e', 'x', 't',
2463 '\r','\n','\r','\n', 0};
2464 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
2465 'S', 'o', 'm', 'e',
2466 'T', 'e', 'x', 't',
2467 '\n','\n', 0};
2468 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
2469 'S', 'o', 'm', 'e',
2470 'T', 'e', 'x', 't',
2471 '\r','\r', 0};
2472 WCHAR TestItem4[] = {'T', 'e', 's', 't',
2473 'S', 'o', 'm', 'e',
2474 'T', 'e', 'x', 't',
2475 '\r','\r','\n','\r',
2476 '\n', 0};
2477 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
2478 'S', 'o', 'm', 'e',
2479 'T', 'e', 'x', 't',
2480 ' ','\r', 0};
2481 #define MAX_BUF_LEN 1024
2482 WCHAR buf[MAX_BUF_LEN];
2483 char * p;
2484 int result;
2485 CHARRANGE cr;
2486 EDITSTREAM es;
2488 setText.codepage = 1200; /* no constant for unicode */
2489 getText.codepage = 1200; /* no constant for unicode */
2490 getText.cb = MAX_BUF_LEN;
2491 getText.flags = GT_DEFAULT;
2492 getText.lpDefaultChar = NULL;
2493 getText.lpUsedDefChar = NULL;
2495 setText.flags = 0;
2496 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2497 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2498 ok(lstrcmpW(buf, TestItem1) == 0,
2499 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2501 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
2502 convert \r to \r\n on return
2504 setText.codepage = 1200; /* no constant for unicode */
2505 getText.codepage = 1200; /* no constant for unicode */
2506 getText.cb = MAX_BUF_LEN;
2507 getText.flags = GT_DEFAULT;
2508 getText.lpDefaultChar = NULL;
2509 getText.lpUsedDefChar = NULL;
2510 setText.flags = 0;
2511 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
2512 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2513 ok(lstrcmpW(buf, TestItem2) == 0,
2514 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2516 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
2517 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
2518 ok(strcmp((const char *)buf, TestItem2_after) == 0,
2519 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
2521 /* Baseline test for just-enough buffer space for string */
2522 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2523 getText.codepage = 1200; /* no constant for unicode */
2524 getText.flags = GT_DEFAULT;
2525 getText.lpDefaultChar = NULL;
2526 getText.lpUsedDefChar = NULL;
2527 memset(buf, 0, MAX_BUF_LEN);
2528 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2529 ok(lstrcmpW(buf, TestItem2) == 0,
2530 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2532 /* When there is enough space for one character, but not both, of the CRLF
2533 pair at the end of the string, the CR is not copied at all. That is,
2534 the caller must not see CRLF pairs truncated to CR at the end of the
2535 string.
2537 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2538 getText.codepage = 1200; /* no constant for unicode */
2539 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
2540 getText.lpDefaultChar = NULL;
2541 getText.lpUsedDefChar = NULL;
2542 memset(buf, 0, MAX_BUF_LEN);
2543 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2544 ok(lstrcmpW(buf, TestItem1) == 0,
2545 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2548 /* \r\n pairs get changed into \r */
2549 setText.codepage = 1200; /* no constant for unicode */
2550 getText.codepage = 1200; /* no constant for unicode */
2551 getText.cb = MAX_BUF_LEN;
2552 getText.flags = GT_DEFAULT;
2553 getText.lpDefaultChar = NULL;
2554 getText.lpUsedDefChar = NULL;
2555 setText.flags = 0;
2556 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
2557 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2558 ok(lstrcmpW(buf, TestItem3_after) == 0,
2559 "EM_SETTEXTEX did not convert properly\n");
2561 /* \n also gets changed to \r */
2562 setText.codepage = 1200; /* no constant for unicode */
2563 getText.codepage = 1200; /* no constant for unicode */
2564 getText.cb = MAX_BUF_LEN;
2565 getText.flags = GT_DEFAULT;
2566 getText.lpDefaultChar = NULL;
2567 getText.lpUsedDefChar = NULL;
2568 setText.flags = 0;
2569 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
2570 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2571 ok(lstrcmpW(buf, TestItem3_after) == 0,
2572 "EM_SETTEXTEX did not convert properly\n");
2574 /* \r\r\n gets changed into single space */
2575 setText.codepage = 1200; /* no constant for unicode */
2576 getText.codepage = 1200; /* no constant for unicode */
2577 getText.cb = MAX_BUF_LEN;
2578 getText.flags = GT_DEFAULT;
2579 getText.lpDefaultChar = NULL;
2580 getText.lpUsedDefChar = NULL;
2581 setText.flags = 0;
2582 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
2583 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2584 ok(lstrcmpW(buf, TestItem4_after) == 0,
2585 "EM_SETTEXTEX did not convert properly\n");
2587 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2588 (WPARAM)&setText, (LPARAM) NULL);
2589 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2591 ok (result == 1,
2592 "EM_SETTEXTEX returned %d, instead of 1\n",result);
2593 ok(lstrlenW(buf) == 0,
2594 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
2596 /* put some text back */
2597 setText.flags = 0;
2598 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2599 /* select some text */
2600 cr.cpMax = 1;
2601 cr.cpMin = 3;
2602 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2603 /* replace current selection */
2604 setText.flags = ST_SELECTION;
2605 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2606 (WPARAM)&setText, (LPARAM) NULL);
2607 ok(result == 0,
2608 "EM_SETTEXTEX with NULL lParam to replace selection"
2609 " with no text should return 0. Got %i\n",
2610 result);
2612 /* put some text back */
2613 setText.flags = 0;
2614 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2615 /* select some text */
2616 cr.cpMax = 1;
2617 cr.cpMin = 3;
2618 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2619 /* replace current selection */
2620 setText.flags = ST_SELECTION;
2621 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2622 (WPARAM)&setText, (LPARAM) TestItem1);
2623 /* get text */
2624 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2625 ok(result == lstrlenW(TestItem1),
2626 "EM_SETTEXTEX with NULL lParam to replace selection"
2627 " with no text should return 0. Got %i\n",
2628 result);
2629 ok(lstrlenW(buf) == 22,
2630 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
2631 lstrlenW(buf) );
2633 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
2634 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
2635 p = (char *)buf;
2636 es.dwCookie = (DWORD_PTR)&p;
2637 es.dwError = 0;
2638 es.pfnCallback = test_WM_SETTEXT_esCallback;
2639 memset(buf, 0, sizeof(buf));
2640 SendMessage(hwndRichEdit, EM_STREAMOUT,
2641 (WPARAM)(SF_RTF), (LPARAM)&es);
2642 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
2644 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
2645 getText.codepage = 1200; /* no constant for unicode */
2646 getText.cb = MAX_BUF_LEN;
2647 getText.flags = GT_DEFAULT;
2648 getText.lpDefaultChar = NULL;
2649 getText.lpUsedDefChar = NULL;
2651 setText.flags = 0;
2652 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
2653 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2654 ok(lstrcmpW(buf, TestItem1) == 0,
2655 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2658 DestroyWindow(hwndRichEdit);
2661 static void test_EM_LIMITTEXT(void)
2663 int ret;
2665 HWND hwndRichEdit = new_richedit(NULL);
2667 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
2668 * about setting the length to -1 for multiline edit controls doesn't happen.
2671 /* Don't check default gettextlimit case. That's done in other tests */
2673 /* Set textlimit to 100 */
2674 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
2675 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2676 ok (ret == 100,
2677 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
2679 /* Set textlimit to 0 */
2680 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
2681 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2682 ok (ret == 65536,
2683 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
2685 /* Set textlimit to -1 */
2686 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
2687 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2688 ok (ret == -1,
2689 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
2691 /* Set textlimit to -2 */
2692 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
2693 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2694 ok (ret == -2,
2695 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
2697 DestroyWindow (hwndRichEdit);
2701 static void test_EM_EXLIMITTEXT(void)
2703 int i, selBegin, selEnd, len1, len2;
2704 int result;
2705 char text[1024 + 1];
2706 char buffer[1024 + 1];
2707 int textlimit = 0; /* multiple of 100 */
2708 HWND hwndRichEdit = new_richedit(NULL);
2710 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2711 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
2713 textlimit = 256000;
2714 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2715 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2716 /* set higher */
2717 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2719 textlimit = 1000;
2720 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2721 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2722 /* set lower */
2723 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2725 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
2726 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2727 /* default for WParam = 0 */
2728 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
2730 textlimit = sizeof(text)-1;
2731 memset(text, 'W', textlimit);
2732 text[sizeof(text)-1] = 0;
2733 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2734 /* maxed out text */
2735 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2737 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2738 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2739 len1 = selEnd - selBegin;
2741 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
2742 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
2743 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
2744 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2745 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2746 len2 = selEnd - selBegin;
2748 ok(len1 != len2,
2749 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2750 len1,len2,i);
2752 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2753 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2754 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
2755 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2756 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2757 len1 = selEnd - selBegin;
2759 ok(len1 != len2,
2760 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2761 len1,len2,i);
2763 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2764 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2765 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
2766 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2767 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2768 len2 = selEnd - selBegin;
2770 ok(len1 == len2,
2771 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2772 len1,len2,i);
2774 /* set text up to the limit, select all the text, then add a char */
2775 textlimit = 5;
2776 memset(text, 'W', textlimit);
2777 text[textlimit] = 0;
2778 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2779 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2780 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2781 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2782 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2783 result = strcmp(buffer, "A");
2784 ok(0 == result, "got string = \"%s\"\n", buffer);
2786 /* WM_SETTEXT not limited */
2787 textlimit = 10;
2788 memset(text, 'W', textlimit);
2789 text[textlimit] = 0;
2790 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
2791 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2792 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2793 i = strlen(buffer);
2794 ok(10 == i, "expected 10 chars\n");
2795 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2796 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2798 /* try inserting more text at end */
2799 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2800 ok(0 == i, "WM_CHAR wasn't processed\n");
2801 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2802 i = strlen(buffer);
2803 ok(10 == i, "expected 10 chars, got %i\n", i);
2804 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2805 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2807 /* try inserting text at beginning */
2808 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
2809 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2810 ok(0 == i, "WM_CHAR wasn't processed\n");
2811 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2812 i = strlen(buffer);
2813 ok(10 == i, "expected 10 chars, got %i\n", i);
2814 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2815 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2817 /* WM_CHAR is limited */
2818 textlimit = 1;
2819 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2820 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2821 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2822 ok(0 == i, "WM_CHAR wasn't processed\n");
2823 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2824 ok(0 == i, "WM_CHAR wasn't processed\n");
2825 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2826 i = strlen(buffer);
2827 ok(1 == i, "expected 1 chars, got %i instead\n", i);
2829 DestroyWindow(hwndRichEdit);
2832 static void test_EM_GETLIMITTEXT(void)
2834 int i;
2835 HWND hwndRichEdit = new_richedit(NULL);
2837 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2838 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
2840 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
2841 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2842 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
2844 DestroyWindow(hwndRichEdit);
2847 static void test_WM_SETFONT(void)
2849 /* There is no invalid input or error conditions for this function.
2850 * NULL wParam and lParam just fall back to their default values
2851 * It should be noted that even if you use a gibberish name for your fonts
2852 * here, it will still work because the name is stored. They will display as
2853 * System, but will report their name to be whatever they were created as */
2855 HWND hwndRichEdit = new_richedit(NULL);
2856 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2857 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2858 FF_DONTCARE, "Marlett");
2859 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2860 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2861 FF_DONTCARE, "MS Sans Serif");
2862 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2863 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2864 FF_DONTCARE, "Courier");
2865 LOGFONTA sentLogFont;
2866 CHARFORMAT2A returnedCF2A;
2868 returnedCF2A.cbSize = sizeof(returnedCF2A);
2870 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2871 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
2872 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2874 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
2875 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2876 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
2877 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2879 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
2880 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2881 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
2882 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2883 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
2884 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2886 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
2887 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2888 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
2889 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2890 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
2891 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2893 /* This last test is special since we send in NULL. We clear the variables
2894 * and just compare to "System" instead of the sent in font name. */
2895 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
2896 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
2897 returnedCF2A.cbSize = sizeof(returnedCF2A);
2899 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
2900 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2901 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
2902 ok (!strcmp("System",returnedCF2A.szFaceName),
2903 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
2905 DestroyWindow(hwndRichEdit);
2909 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
2910 LPBYTE pbBuff,
2911 LONG cb,
2912 LONG *pcb)
2914 const char** str = (const char**)dwCookie;
2915 int size = strlen(*str);
2916 if(size > 3) /* let's make it piecemeal for fun */
2917 size = 3;
2918 *pcb = cb;
2919 if (*pcb > size) {
2920 *pcb = size;
2922 if (*pcb > 0) {
2923 memcpy(pbBuff, *str, *pcb);
2924 *str += *pcb;
2926 return 0;
2929 static void test_EM_GETMODIFY(void)
2931 HWND hwndRichEdit = new_richedit(NULL);
2932 LRESULT result;
2933 SETTEXTEX setText;
2934 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2935 'S', 'o', 'm', 'e',
2936 'T', 'e', 'x', 't', 0};
2937 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2938 'S', 'o', 'm', 'e',
2939 'O', 't', 'h', 'e', 'r',
2940 'T', 'e', 'x', 't', 0};
2941 const char* streamText = "hello world";
2942 CHARFORMAT2 cf2;
2943 PARAFORMAT2 pf2;
2944 EDITSTREAM es;
2946 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2947 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2948 FF_DONTCARE, "Courier");
2950 setText.codepage = 1200; /* no constant for unicode */
2951 setText.flags = ST_KEEPUNDO;
2954 /* modify flag shouldn't be set when richedit is first created */
2955 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2956 ok (result == 0,
2957 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
2959 /* setting modify flag should actually set it */
2960 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
2961 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2962 ok (result != 0,
2963 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
2965 /* clearing modify flag should actually clear it */
2966 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2967 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2968 ok (result == 0,
2969 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
2971 /* setting font doesn't change modify flag */
2972 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2973 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
2974 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2975 ok (result == 0,
2976 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
2978 /* setting text should set modify flag */
2979 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2980 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2981 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2982 ok (result != 0,
2983 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
2985 /* undo previous text doesn't reset modify flag */
2986 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2987 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2988 ok (result != 0,
2989 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
2991 /* set text with no flag to keep undo stack should not set modify flag */
2992 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2993 setText.flags = 0;
2994 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2995 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2996 ok (result == 0,
2997 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
2999 /* WM_SETTEXT doesn't modify */
3000 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3001 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3002 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3003 ok (result == 0,
3004 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3006 /* clear the text */
3007 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3008 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3009 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3010 ok (result == 0,
3011 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3013 /* replace text */
3014 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3015 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3016 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3017 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3018 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3019 ok (result != 0,
3020 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3022 /* copy/paste text 1 */
3023 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3024 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3025 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3026 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3027 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3028 ok (result != 0,
3029 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3031 /* copy/paste text 2 */
3032 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3033 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3034 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3035 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3036 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3037 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3038 ok (result != 0,
3039 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3041 /* press char */
3042 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3043 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3044 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3045 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3046 ok (result != 0,
3047 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3049 /* press del */
3050 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3051 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3052 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
3053 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3054 ok (result != 0,
3055 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3057 /* set char format */
3058 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3059 cf2.cbSize = sizeof(CHARFORMAT2);
3060 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3061 (LPARAM) &cf2);
3062 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3063 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3064 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3065 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3066 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3067 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3068 ok (result != 0,
3069 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3071 /* set para format */
3072 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3073 pf2.cbSize = sizeof(PARAFORMAT2);
3074 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3075 (LPARAM) &pf2);
3076 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3077 pf2.wAlignment = PFA_RIGHT;
3078 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3079 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3080 ok (result == 0,
3081 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3083 /* EM_STREAM */
3084 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3085 es.dwCookie = (DWORD_PTR)&streamText;
3086 es.dwError = 0;
3087 es.pfnCallback = test_EM_GETMODIFY_esCallback;
3088 SendMessage(hwndRichEdit, EM_STREAMIN,
3089 (WPARAM)(SF_TEXT), (LPARAM)&es);
3090 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3091 ok (result != 0,
3092 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
3094 DestroyWindow(hwndRichEdit);
3097 struct exsetsel_s {
3098 long min;
3099 long max;
3100 long expected_retval;
3101 int expected_getsel_start;
3102 int expected_getsel_end;
3103 int _exsetsel_todo_wine;
3104 int _getsel_todo_wine;
3107 const struct exsetsel_s exsetsel_tests[] = {
3108 /* sanity tests */
3109 {5, 10, 10, 5, 10, 0, 0},
3110 {15, 17, 17, 15, 17, 0, 0},
3111 /* test cpMax > strlen() */
3112 {0, 100, 18, 0, 18, 0, 1},
3113 /* test cpMin == cpMax */
3114 {5, 5, 5, 5, 5, 0, 0},
3115 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
3116 {-1, 0, 5, 5, 5, 0, 0},
3117 {-1, 17, 5, 5, 5, 0, 0},
3118 {-1, 18, 5, 5, 5, 0, 0},
3119 /* test cpMin < 0 && cpMax < 0 */
3120 {-1, -1, 17, 17, 17, 0, 0},
3121 {-4, -5, 17, 17, 17, 0, 0},
3122 /* test cMin >=0 && cpMax < 0 (bug 6814) */
3123 {0, -1, 18, 0, 18, 0, 1},
3124 {17, -5, 18, 17, 18, 0, 1},
3125 {18, -3, 17, 17, 17, 0, 0},
3126 /* test if cpMin > cpMax */
3127 {15, 19, 18, 15, 18, 0, 1},
3128 {19, 15, 18, 15, 18, 0, 1}
3131 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
3132 CHARRANGE cr;
3133 long result;
3134 int start, end;
3136 cr.cpMin = setsel->min;
3137 cr.cpMax = setsel->max;
3138 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
3140 if (setsel->_exsetsel_todo_wine) {
3141 todo_wine {
3142 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3144 } else {
3145 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3148 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
3150 if (setsel->_getsel_todo_wine) {
3151 todo_wine {
3152 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);
3154 } else {
3155 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);
3159 static void test_EM_EXSETSEL(void)
3161 HWND hwndRichEdit = new_richedit(NULL);
3162 int i;
3163 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
3165 /* sending some text to the window */
3166 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3167 /* 01234567890123456*/
3168 /* 10 */
3170 for (i = 0; i < num_tests; i++) {
3171 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
3174 DestroyWindow(hwndRichEdit);
3177 static void test_EM_REPLACESEL(int redraw)
3179 HWND hwndRichEdit = new_richedit(NULL);
3180 char buffer[1024] = {0};
3181 int r;
3182 GETTEXTEX getText;
3183 CHARRANGE cr;
3185 /* sending some text to the window */
3186 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3187 /* 01234567890123456*/
3188 /* 10 */
3190 /* FIXME add more tests */
3191 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
3192 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
3193 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
3194 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3195 r = strcmp(buffer, "testing");
3196 ok(0 == r, "expected %d, got %d\n", 0, r);
3198 DestroyWindow(hwndRichEdit);
3200 hwndRichEdit = new_richedit(NULL);
3202 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
3203 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
3205 /* Test behavior with carriage returns and newlines */
3206 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3207 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
3208 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
3209 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3210 r = strcmp(buffer, "RichEdit1");
3211 ok(0 == r, "expected %d, got %d\n", 0, r);
3212 getText.cb = 1024;
3213 getText.codepage = CP_ACP;
3214 getText.flags = GT_DEFAULT;
3215 getText.lpDefaultChar = NULL;
3216 getText.lpUsedDefChar = NULL;
3217 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3218 ok(strcmp(buffer, "RichEdit1") == 0,
3219 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
3221 /* Test number of lines reported after EM_REPLACESEL */
3222 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3223 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3225 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3226 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
3227 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
3228 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3229 r = strcmp(buffer, "RichEdit1\r\n");
3230 ok(0 == r, "expected %d, got %d\n", 0, r);
3231 getText.cb = 1024;
3232 getText.codepage = CP_ACP;
3233 getText.flags = GT_DEFAULT;
3234 getText.lpDefaultChar = NULL;
3235 getText.lpUsedDefChar = NULL;
3236 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3237 ok(strcmp(buffer, "RichEdit1\r") == 0,
3238 "EM_GETTEXTEX returned incorrect string\n");
3240 /* Test number of lines reported after EM_REPLACESEL */
3241 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3242 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3244 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
3245 EM_REPLACESEL. The general rule seems to be that Win98's riched20
3246 returns the number of characters *inserted* into the control (after
3247 required conversions), but WinXP's riched20 returns the number of
3248 characters interpreted from the original lParam. Wine's builtin riched20
3249 implements the WinXP behavior.
3251 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3252 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
3253 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
3254 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
3256 /* Test number of lines reported after EM_REPLACESEL */
3257 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3258 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3260 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3261 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3262 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3263 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3265 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3266 r = strcmp(buffer, "RichEdit1\r\n");
3267 ok(0 == r, "expected %d, got %d\n", 0, r);
3268 getText.cb = 1024;
3269 getText.codepage = CP_ACP;
3270 getText.flags = GT_DEFAULT;
3271 getText.lpDefaultChar = NULL;
3272 getText.lpUsedDefChar = NULL;
3273 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3274 ok(strcmp(buffer, "RichEdit1\r") == 0,
3275 "EM_GETTEXTEX returned incorrect string\n");
3277 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3278 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3279 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3280 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3282 /* The following tests show that richedit should handle the special \r\r\n
3283 sequence by turning it into a single space on insertion. However,
3284 EM_REPLACESEL on WinXP returns the number of characters in the original
3285 string.
3288 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3289 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
3290 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
3291 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3292 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3293 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3294 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3296 /* Test the actual string */
3297 getText.cb = 1024;
3298 getText.codepage = CP_ACP;
3299 getText.flags = GT_DEFAULT;
3300 getText.lpDefaultChar = NULL;
3301 getText.lpUsedDefChar = NULL;
3302 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3303 ok(strcmp(buffer, "\r\r") == 0,
3304 "EM_GETTEXTEX returned incorrect string\n");
3306 /* Test number of lines reported after EM_REPLACESEL */
3307 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3308 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3310 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3311 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
3312 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
3313 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
3314 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3315 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3316 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
3317 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
3319 /* Test the actual string */
3320 getText.cb = 1024;
3321 getText.codepage = CP_ACP;
3322 getText.flags = GT_DEFAULT;
3323 getText.lpDefaultChar = NULL;
3324 getText.lpUsedDefChar = NULL;
3325 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3326 ok(strcmp(buffer, " ") == 0,
3327 "EM_GETTEXTEX returned incorrect string\n");
3329 /* Test number of lines reported after EM_REPLACESEL */
3330 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3331 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3333 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3334 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
3335 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3336 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3337 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3338 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3339 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3340 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3342 /* Test the actual string */
3343 getText.cb = 1024;
3344 getText.codepage = CP_ACP;
3345 getText.flags = GT_DEFAULT;
3346 getText.lpDefaultChar = NULL;
3347 getText.lpUsedDefChar = NULL;
3348 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3349 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
3350 "EM_GETTEXTEX returned incorrect string\n");
3352 /* Test number of lines reported after EM_REPLACESEL */
3353 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3354 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3356 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3357 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
3358 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
3359 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
3360 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3361 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3362 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3363 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3365 /* Test the actual string */
3366 getText.cb = 1024;
3367 getText.codepage = CP_ACP;
3368 getText.flags = GT_DEFAULT;
3369 getText.lpDefaultChar = NULL;
3370 getText.lpUsedDefChar = NULL;
3371 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3372 ok(strcmp(buffer, " \r") == 0,
3373 "EM_GETTEXTEX returned incorrect string\n");
3375 /* Test number of lines reported after EM_REPLACESEL */
3376 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3377 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3379 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3380 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
3381 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
3382 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
3383 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3384 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3385 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
3386 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
3388 /* Test the actual string */
3389 getText.cb = 1024;
3390 getText.codepage = CP_ACP;
3391 getText.flags = GT_DEFAULT;
3392 getText.lpDefaultChar = NULL;
3393 getText.lpUsedDefChar = NULL;
3394 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3395 ok(strcmp(buffer, " \r\r") == 0,
3396 "EM_GETTEXTEX returned incorrect string\n");
3398 /* Test number of lines reported after EM_REPLACESEL */
3399 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3400 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3402 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3403 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
3404 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
3405 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
3406 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3407 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3408 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
3409 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
3411 /* Test the actual string */
3412 getText.cb = 1024;
3413 getText.codepage = CP_ACP;
3414 getText.flags = GT_DEFAULT;
3415 getText.lpDefaultChar = NULL;
3416 getText.lpUsedDefChar = NULL;
3417 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3418 ok(strcmp(buffer, "\rX\r\r\r") == 0,
3419 "EM_GETTEXTEX returned incorrect string\n");
3421 /* Test number of lines reported after EM_REPLACESEL */
3422 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3423 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
3425 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3426 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
3427 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
3428 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3429 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3430 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3431 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3433 /* Test the actual string */
3434 getText.cb = 1024;
3435 getText.codepage = CP_ACP;
3436 getText.flags = GT_DEFAULT;
3437 getText.lpDefaultChar = NULL;
3438 getText.lpUsedDefChar = NULL;
3439 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3440 ok(strcmp(buffer, "\r\r") == 0,
3441 "EM_GETTEXTEX returned incorrect string\n");
3443 /* Test number of lines reported after EM_REPLACESEL */
3444 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3445 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3447 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3448 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
3449 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3450 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3451 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3452 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3453 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3454 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3456 /* Test the actual string */
3457 getText.cb = 1024;
3458 getText.codepage = CP_ACP;
3459 getText.flags = GT_DEFAULT;
3460 getText.lpDefaultChar = NULL;
3461 getText.lpUsedDefChar = NULL;
3462 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3463 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
3464 "EM_GETTEXTEX returned incorrect string\n");
3466 /* Test number of lines reported after EM_REPLACESEL */
3467 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3468 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3470 if (!redraw)
3471 /* This is needed to avoid interferring with keybd_event calls
3472 * on other tests that simulate keyboard events. */
3473 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
3475 DestroyWindow(hwndRichEdit);
3478 static void test_WM_PASTE(void)
3480 int result;
3481 char buffer[1024] = {0};
3482 const char* text1 = "testing paste\r";
3483 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
3484 const char* text1_after = "testing paste\r\n";
3485 const char* text2 = "testing paste\r\rtesting paste";
3486 const char* text2_after = "testing paste\r\n\r\ntesting paste";
3487 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
3488 HWND hwndRichEdit = new_richedit(NULL);
3490 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
3491 messages, probably because it inspects the keyboard state itself.
3492 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
3495 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
3496 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
3497 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
3498 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
3499 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
3501 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
3502 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
3504 SEND_CTRL_C(hwndRichEdit); /* Copy */
3505 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3506 SEND_CTRL_V(hwndRichEdit); /* Paste */
3507 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3508 /* Pasted text should be visible at this step */
3509 result = strcmp(text1_step1, buffer);
3510 ok(result == 0,
3511 "test paste: strcmp = %i\n", result);
3512 SEND_CTRL_Z(hwndRichEdit); /* Undo */
3513 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3514 /* Text should be the same as before (except for \r -> \r\n conversion) */
3515 result = strcmp(text1_after, buffer);
3516 ok(result == 0,
3517 "test paste: strcmp = %i\n", result);
3519 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
3520 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
3521 SEND_CTRL_C(hwndRichEdit); /* Copy */
3522 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3523 SEND_CTRL_V(hwndRichEdit); /* Paste */
3524 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3525 /* Pasted text should be visible at this step */
3526 result = strcmp(text3, buffer);
3527 ok(result == 0,
3528 "test paste: strcmp = %i\n", result);
3529 SEND_CTRL_Z(hwndRichEdit); /* Undo */
3530 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3531 /* Text should be the same as before (except for \r -> \r\n conversion) */
3532 result = strcmp(text2_after, buffer);
3533 ok(result == 0,
3534 "test paste: strcmp = %i\n", result);
3535 SEND_CTRL_Y(hwndRichEdit); /* Redo */
3536 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3537 /* Text should revert to post-paste state */
3538 result = strcmp(buffer,text3);
3539 ok(result == 0,
3540 "test paste: strcmp = %i\n", result);
3542 DestroyWindow(hwndRichEdit);
3545 static void test_EM_FORMATRANGE(void)
3547 int r;
3548 FORMATRANGE fr;
3549 HDC hdc;
3550 HWND hwndRichEdit = new_richedit(NULL);
3552 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
3554 hdc = GetDC(hwndRichEdit);
3555 ok(hdc != NULL, "Could not get HDC\n");
3557 fr.hdc = fr.hdcTarget = hdc;
3558 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
3559 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
3560 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
3561 fr.chrg.cpMin = 0;
3562 fr.chrg.cpMax = 20;
3564 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3565 todo_wine {
3566 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3569 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3570 todo_wine {
3571 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
3574 fr.chrg.cpMin = 0;
3575 fr.chrg.cpMax = 10;
3577 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3578 todo_wine {
3579 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
3582 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3583 todo_wine {
3584 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3587 DestroyWindow(hwndRichEdit);
3590 static int nCallbackCount = 0;
3592 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
3593 LONG cb, LONG* pcb)
3595 const char text[] = {'t','e','s','t'};
3597 if (sizeof(text) <= cb)
3599 if ((int)dwCookie != nCallbackCount)
3601 *pcb = 0;
3602 return 0;
3605 memcpy (pbBuff, text, sizeof(text));
3606 *pcb = sizeof(text);
3608 nCallbackCount++;
3610 return 0;
3612 else
3613 return 1; /* indicates callback failed */
3616 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
3617 LPBYTE pbBuff,
3618 LONG cb,
3619 LONG *pcb)
3621 const char** str = (const char**)dwCookie;
3622 int size = strlen(*str);
3623 *pcb = cb;
3624 if (*pcb > size) {
3625 *pcb = size;
3627 if (*pcb > 0) {
3628 memcpy(pbBuff, *str, *pcb);
3629 *str += *pcb;
3631 return 0;
3634 struct StringWithLength {
3635 int length;
3636 char *buffer;
3639 /* This callback is used to handled the null characters in a string. */
3640 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
3641 LPBYTE pbBuff,
3642 LONG cb,
3643 LONG *pcb)
3645 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
3646 int size = str->length;
3647 *pcb = cb;
3648 if (*pcb > size) {
3649 *pcb = size;
3651 if (*pcb > 0) {
3652 memcpy(pbBuff, str->buffer, *pcb);
3653 str->buffer += *pcb;
3654 str->length -= *pcb;
3656 return 0;
3659 static void test_EM_STREAMIN(void)
3661 HWND hwndRichEdit = new_richedit(NULL);
3662 LRESULT result;
3663 EDITSTREAM es;
3664 char buffer[1024] = {0};
3666 const char * streamText0 = "{\\rtf1 TestSomeText}";
3667 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
3668 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
3670 const char * streamText1 =
3671 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
3672 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
3673 "}\r\n";
3675 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
3676 const char * streamText2 =
3677 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
3678 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
3679 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
3680 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
3681 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
3682 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
3683 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
3685 const char * streamText3 = "RichEdit1";
3687 struct StringWithLength cookieForStream4;
3688 const char * streamText4 =
3689 "This text just needs to be long enough to cause run to be split onto "\
3690 "two seperate lines and make sure the null terminating character is "\
3691 "handled properly.\0";
3692 int length4 = strlen(streamText4) + 1;
3693 cookieForStream4.buffer = (char *)streamText4;
3694 cookieForStream4.length = length4;
3696 /* Minimal test without \par at the end */
3697 es.dwCookie = (DWORD_PTR)&streamText0;
3698 es.dwError = 0;
3699 es.pfnCallback = test_EM_STREAMIN_esCallback;
3700 SendMessage(hwndRichEdit, EM_STREAMIN,
3701 (WPARAM)(SF_RTF), (LPARAM)&es);
3703 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3704 ok (result == 12,
3705 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
3706 result = strcmp (buffer,"TestSomeText");
3707 ok (result == 0,
3708 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
3709 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
3711 /* Native richedit 2.0 ignores last \par */
3712 es.dwCookie = (DWORD_PTR)&streamText0a;
3713 es.dwError = 0;
3714 es.pfnCallback = test_EM_STREAMIN_esCallback;
3715 SendMessage(hwndRichEdit, EM_STREAMIN,
3716 (WPARAM)(SF_RTF), (LPARAM)&es);
3718 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3719 ok (result == 12,
3720 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
3721 result = strcmp (buffer,"TestSomeText");
3722 ok (result == 0,
3723 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
3724 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
3726 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
3727 es.dwCookie = (DWORD_PTR)&streamText0b;
3728 es.dwError = 0;
3729 es.pfnCallback = test_EM_STREAMIN_esCallback;
3730 SendMessage(hwndRichEdit, EM_STREAMIN,
3731 (WPARAM)(SF_RTF), (LPARAM)&es);
3733 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3734 ok (result == 14,
3735 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
3736 result = strcmp (buffer,"TestSomeText\r\n");
3737 ok (result == 0,
3738 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
3739 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
3741 es.dwCookie = (DWORD_PTR)&streamText1;
3742 es.dwError = 0;
3743 es.pfnCallback = test_EM_STREAMIN_esCallback;
3744 SendMessage(hwndRichEdit, EM_STREAMIN,
3745 (WPARAM)(SF_RTF), (LPARAM)&es);
3747 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3748 ok (result == 12,
3749 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
3750 result = strcmp (buffer,"TestSomeText");
3751 ok (result == 0,
3752 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3753 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
3755 es.dwCookie = (DWORD_PTR)&streamText2;
3756 es.dwError = 0;
3757 SendMessage(hwndRichEdit, EM_STREAMIN,
3758 (WPARAM)(SF_RTF), (LPARAM)&es);
3760 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3761 ok (result == 0,
3762 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
3763 ok (strlen(buffer) == 0,
3764 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3765 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
3767 es.dwCookie = (DWORD_PTR)&streamText3;
3768 es.dwError = 0;
3769 SendMessage(hwndRichEdit, EM_STREAMIN,
3770 (WPARAM)(SF_RTF), (LPARAM)&es);
3772 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3773 ok (result == 0,
3774 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
3775 ok (strlen(buffer) == 0,
3776 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
3777 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
3779 es.dwCookie = (DWORD_PTR)&cookieForStream4;
3780 es.dwError = 0;
3781 es.pfnCallback = test_EM_STREAMIN_esCallback2;
3782 SendMessage(hwndRichEdit, EM_STREAMIN,
3783 (WPARAM)(SF_TEXT), (LPARAM)&es);
3785 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3786 ok (result == length4,
3787 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
3788 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
3790 DestroyWindow(hwndRichEdit);
3793 static void test_EM_StreamIn_Undo(void)
3795 /* The purpose of this test is to determine when a EM_StreamIn should be
3796 * undoable. This is important because WM_PASTE currently uses StreamIn and
3797 * pasting should always be undoable but streaming isn't always.
3799 * cases to test:
3800 * StreamIn plain text without SFF_SELECTION.
3801 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
3802 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
3803 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
3804 * Feel free to add tests for other text modes or StreamIn things.
3808 HWND hwndRichEdit = new_richedit(NULL);
3809 LRESULT result;
3810 EDITSTREAM es;
3811 char buffer[1024] = {0};
3812 const char randomtext[] = "Some text";
3814 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
3816 /* StreamIn, no SFF_SELECTION */
3817 es.dwCookie = nCallbackCount;
3818 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3819 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3820 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3821 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
3822 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3823 result = strcmp (buffer,"test");
3824 ok (result == 0,
3825 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3827 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3828 ok (result == FALSE,
3829 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
3831 /* StreamIn, SFF_SELECTION, but nothing selected */
3832 es.dwCookie = nCallbackCount;
3833 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3834 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3835 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3836 SendMessage(hwndRichEdit, EM_STREAMIN,
3837 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3838 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3839 result = strcmp (buffer,"testSome text");
3840 ok (result == 0,
3841 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3843 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3844 ok (result == TRUE,
3845 "EM_STREAMIN with SFF_SELECTION but no selection set "
3846 "should create an undo\n");
3848 /* StreamIn, SFF_SELECTION, with a selection */
3849 es.dwCookie = nCallbackCount;
3850 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3851 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3852 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
3853 SendMessage(hwndRichEdit, EM_STREAMIN,
3854 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3855 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3856 result = strcmp (buffer,"Sometesttext");
3857 ok (result == 0,
3858 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3860 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3861 ok (result == TRUE,
3862 "EM_STREAMIN with SFF_SELECTION and selection set "
3863 "should create an undo\n");
3865 DestroyWindow(hwndRichEdit);
3868 static BOOL is_em_settextex_supported(HWND hwnd)
3870 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
3871 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
3874 static void test_unicode_conversions(void)
3876 static const WCHAR tW[] = {'t',0};
3877 static const WCHAR teW[] = {'t','e',0};
3878 static const WCHAR textW[] = {'t','e','s','t',0};
3879 static const char textA[] = "test";
3880 char bufA[64];
3881 WCHAR bufW[64];
3882 HWND hwnd;
3883 int is_win9x, em_settextex_supported, ret;
3885 is_win9x = GetVersion() & 0x80000000;
3887 #define set_textA(hwnd, wm_set_text, txt) \
3888 do { \
3889 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
3890 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3891 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3892 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3893 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
3894 } while(0)
3895 #define expect_textA(hwnd, wm_get_text, txt) \
3896 do { \
3897 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3898 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3899 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3900 memset(bufA, 0xAA, sizeof(bufA)); \
3901 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3902 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3903 ret = lstrcmpA(bufA, txt); \
3904 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
3905 } while(0)
3907 #define set_textW(hwnd, wm_set_text, txt) \
3908 do { \
3909 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
3910 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3911 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3912 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3913 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
3914 } while(0)
3915 #define expect_textW(hwnd, wm_get_text, txt) \
3916 do { \
3917 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
3918 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3919 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3920 memset(bufW, 0xAA, sizeof(bufW)); \
3921 if (is_win9x) \
3923 assert(wm_get_text == EM_GETTEXTEX); \
3924 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3925 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3927 else \
3929 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3930 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
3932 ret = lstrcmpW(bufW, txt); \
3933 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
3934 } while(0)
3935 #define expect_empty(hwnd, wm_get_text) \
3936 do { \
3937 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3938 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3939 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3940 memset(bufA, 0xAA, sizeof(bufA)); \
3941 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3942 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
3943 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
3944 } while(0)
3946 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3947 0, 0, 200, 60, 0, 0, 0, 0);
3948 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3950 ret = IsWindowUnicode(hwnd);
3951 if (is_win9x)
3952 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
3953 else
3954 ok(ret, "RichEdit20W should be unicode under NT\n");
3956 /* EM_SETTEXTEX is supported starting from version 3.0 */
3957 em_settextex_supported = is_em_settextex_supported(hwnd);
3958 trace("EM_SETTEXTEX is %ssupported on this platform\n",
3959 em_settextex_supported ? "" : "NOT ");
3961 expect_empty(hwnd, WM_GETTEXT);
3962 expect_empty(hwnd, EM_GETTEXTEX);
3964 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
3965 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3966 expect_textA(hwnd, WM_GETTEXT, "t");
3967 expect_textA(hwnd, EM_GETTEXTEX, "t");
3968 expect_textW(hwnd, EM_GETTEXTEX, tW);
3970 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
3971 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3972 expect_textA(hwnd, WM_GETTEXT, "te");
3973 expect_textA(hwnd, EM_GETTEXTEX, "te");
3974 expect_textW(hwnd, EM_GETTEXTEX, teW);
3976 set_textA(hwnd, WM_SETTEXT, NULL);
3977 expect_empty(hwnd, WM_GETTEXT);
3978 expect_empty(hwnd, EM_GETTEXTEX);
3980 if (is_win9x)
3981 set_textA(hwnd, WM_SETTEXT, textW);
3982 else
3983 set_textA(hwnd, WM_SETTEXT, textA);
3984 expect_textA(hwnd, WM_GETTEXT, textA);
3985 expect_textA(hwnd, EM_GETTEXTEX, textA);
3986 expect_textW(hwnd, EM_GETTEXTEX, textW);
3988 if (em_settextex_supported)
3990 set_textA(hwnd, EM_SETTEXTEX, textA);
3991 expect_textA(hwnd, WM_GETTEXT, textA);
3992 expect_textA(hwnd, EM_GETTEXTEX, textA);
3993 expect_textW(hwnd, EM_GETTEXTEX, textW);
3996 if (!is_win9x)
3998 set_textW(hwnd, WM_SETTEXT, textW);
3999 expect_textW(hwnd, WM_GETTEXT, textW);
4000 expect_textA(hwnd, WM_GETTEXT, textA);
4001 expect_textW(hwnd, EM_GETTEXTEX, textW);
4002 expect_textA(hwnd, EM_GETTEXTEX, textA);
4004 if (em_settextex_supported)
4006 set_textW(hwnd, EM_SETTEXTEX, textW);
4007 expect_textW(hwnd, WM_GETTEXT, textW);
4008 expect_textA(hwnd, WM_GETTEXT, textA);
4009 expect_textW(hwnd, EM_GETTEXTEX, textW);
4010 expect_textA(hwnd, EM_GETTEXTEX, textA);
4013 DestroyWindow(hwnd);
4015 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
4016 0, 0, 200, 60, 0, 0, 0, 0);
4017 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4019 ret = IsWindowUnicode(hwnd);
4020 ok(!ret, "RichEdit20A should NOT be unicode\n");
4022 set_textA(hwnd, WM_SETTEXT, textA);
4023 expect_textA(hwnd, WM_GETTEXT, textA);
4024 expect_textA(hwnd, EM_GETTEXTEX, textA);
4025 expect_textW(hwnd, EM_GETTEXTEX, textW);
4027 if (em_settextex_supported)
4029 set_textA(hwnd, EM_SETTEXTEX, textA);
4030 expect_textA(hwnd, WM_GETTEXT, textA);
4031 expect_textA(hwnd, EM_GETTEXTEX, textA);
4032 expect_textW(hwnd, EM_GETTEXTEX, textW);
4035 if (!is_win9x)
4037 set_textW(hwnd, WM_SETTEXT, textW);
4038 expect_textW(hwnd, WM_GETTEXT, textW);
4039 expect_textA(hwnd, WM_GETTEXT, textA);
4040 expect_textW(hwnd, EM_GETTEXTEX, textW);
4041 expect_textA(hwnd, EM_GETTEXTEX, textA);
4043 if (em_settextex_supported)
4045 set_textW(hwnd, EM_SETTEXTEX, textW);
4046 expect_textW(hwnd, WM_GETTEXT, textW);
4047 expect_textA(hwnd, WM_GETTEXT, textA);
4048 expect_textW(hwnd, EM_GETTEXTEX, textW);
4049 expect_textA(hwnd, EM_GETTEXTEX, textA);
4052 DestroyWindow(hwnd);
4055 static void test_WM_CHAR(void)
4057 HWND hwnd;
4058 int ret;
4059 const char * char_list = "abc\rabc\r";
4060 const char * expected_content_single = "abcabc";
4061 const char * expected_content_multi = "abc\r\nabc\r\n";
4062 char buffer[64] = {0};
4063 const char * p;
4065 /* single-line control must IGNORE carriage returns */
4066 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4067 0, 0, 200, 60, 0, 0, 0, 0);
4068 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4070 p = char_list;
4071 while (*p != '\0') {
4072 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4073 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4074 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4075 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4076 p++;
4079 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4080 ret = strcmp(buffer, expected_content_single);
4081 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4083 DestroyWindow(hwnd);
4085 /* multi-line control inserts CR normally */
4086 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4087 0, 0, 200, 60, 0, 0, 0, 0);
4088 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4090 p = char_list;
4091 while (*p != '\0') {
4092 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4093 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4094 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4095 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4096 p++;
4099 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4100 ret = strcmp(buffer, expected_content_multi);
4101 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4103 DestroyWindow(hwnd);
4106 static void test_EM_GETTEXTLENGTHEX(void)
4108 HWND hwnd;
4109 GETTEXTLENGTHEX gtl;
4110 int ret;
4111 const char * base_string = "base string";
4112 const char * test_string = "a\nb\n\n\r\n";
4113 const char * test_string_after = "a";
4114 const char * test_string_2 = "a\rtest\rstring";
4115 char buffer[64] = {0};
4117 /* single line */
4118 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4119 0, 0, 200, 60, 0, 0, 0, 0);
4120 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4122 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4123 gtl.codepage = CP_ACP;
4124 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4125 ok(ret == 0, "ret %d\n",ret);
4127 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4128 gtl.codepage = CP_ACP;
4129 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4130 ok(ret == 0, "ret %d\n",ret);
4132 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4134 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4135 gtl.codepage = CP_ACP;
4136 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4137 ok(ret == strlen(base_string), "ret %d\n",ret);
4139 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4140 gtl.codepage = CP_ACP;
4141 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4142 ok(ret == strlen(base_string), "ret %d\n",ret);
4144 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4146 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4147 gtl.codepage = CP_ACP;
4148 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4149 ok(ret == 1, "ret %d\n",ret);
4151 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4152 gtl.codepage = CP_ACP;
4153 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4154 ok(ret == 1, "ret %d\n",ret);
4156 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4157 ret = strcmp(buffer, test_string_after);
4158 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4160 DestroyWindow(hwnd);
4162 /* multi line */
4163 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
4164 0, 0, 200, 60, 0, 0, 0, 0);
4165 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4167 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4168 gtl.codepage = CP_ACP;
4169 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4170 ok(ret == 0, "ret %d\n",ret);
4172 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4173 gtl.codepage = CP_ACP;
4174 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4175 ok(ret == 0, "ret %d\n",ret);
4177 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4179 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4180 gtl.codepage = CP_ACP;
4181 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4182 ok(ret == strlen(base_string), "ret %d\n",ret);
4184 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4185 gtl.codepage = CP_ACP;
4186 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4187 ok(ret == strlen(base_string), "ret %d\n",ret);
4189 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
4191 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4192 gtl.codepage = CP_ACP;
4193 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4194 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
4196 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4197 gtl.codepage = CP_ACP;
4198 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4199 ok(ret == strlen(test_string_2), "ret %d\n",ret);
4201 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4203 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4204 gtl.codepage = CP_ACP;
4205 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4206 ok(ret == 10, "ret %d\n",ret);
4208 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4209 gtl.codepage = CP_ACP;
4210 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4211 ok(ret == 6, "ret %d\n",ret);
4213 DestroyWindow(hwnd);
4217 /* globals that parent and child access when checking event masks & notifications */
4218 static HWND eventMaskEditHwnd = 0;
4219 static int queriedEventMask;
4220 static int watchForEventMask = 0;
4222 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
4223 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4225 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
4227 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4229 return DefWindowProcA(hwnd, message, wParam, lParam);
4232 /* test event masks in combination with WM_COMMAND */
4233 static void test_eventMask(void)
4235 HWND parent;
4236 int ret;
4237 WNDCLASSA cls;
4238 const char text[] = "foo bar\n";
4239 int eventMask;
4241 /* register class to capture WM_COMMAND */
4242 cls.style = 0;
4243 cls.lpfnWndProc = ParentMsgCheckProcA;
4244 cls.cbClsExtra = 0;
4245 cls.cbWndExtra = 0;
4246 cls.hInstance = GetModuleHandleA(0);
4247 cls.hIcon = 0;
4248 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4249 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4250 cls.lpszMenuName = NULL;
4251 cls.lpszClassName = "EventMaskParentClass";
4252 if(!RegisterClassA(&cls)) assert(0);
4254 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4255 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4256 ok (parent != 0, "Failed to create parent window\n");
4258 eventMaskEditHwnd = new_richedit(parent);
4259 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
4261 eventMask = ENM_CHANGE | ENM_UPDATE;
4262 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
4263 ok(ret == ENM_NONE, "wrong event mask\n");
4264 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4265 ok(ret == eventMask, "failed to set event mask\n");
4267 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
4268 queriedEventMask = 0; /* initialize to something other than we expect */
4269 watchForEventMask = EN_CHANGE;
4270 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
4271 ok(ret == TRUE, "failed to set text\n");
4272 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
4273 notification in response to WM_SETTEXT */
4274 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
4275 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
4279 static int received_WM_NOTIFY = 0;
4280 static int modify_at_WM_NOTIFY = 0;
4281 static HWND hwndRichedit_WM_NOTIFY;
4283 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4285 if(message == WM_NOTIFY)
4287 received_WM_NOTIFY = 1;
4288 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
4290 return DefWindowProcA(hwnd, message, wParam, lParam);
4293 static void test_WM_NOTIFY(void)
4295 HWND parent;
4296 WNDCLASSA cls;
4297 CHARFORMAT2 cf2;
4299 /* register class to capture WM_NOTIFY */
4300 cls.style = 0;
4301 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
4302 cls.cbClsExtra = 0;
4303 cls.cbWndExtra = 0;
4304 cls.hInstance = GetModuleHandleA(0);
4305 cls.hIcon = 0;
4306 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4307 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4308 cls.lpszMenuName = NULL;
4309 cls.lpszClassName = "WM_NOTIFY_ParentClass";
4310 if(!RegisterClassA(&cls)) assert(0);
4312 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4313 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4314 ok (parent != 0, "Failed to create parent window\n");
4316 hwndRichedit_WM_NOTIFY = new_richedit(parent);
4317 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
4319 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
4321 /* Notifications for selection change should only be sent when selection
4322 actually changes. EM_SETCHARFORMAT is one message that calls
4323 ME_CommitUndo, which should check whether message should be sent */
4324 received_WM_NOTIFY = 0;
4325 cf2.cbSize = sizeof(CHARFORMAT2);
4326 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4327 (LPARAM) &cf2);
4328 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4329 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4330 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
4331 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4333 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
4334 already at 0. */
4335 received_WM_NOTIFY = 0;
4336 modify_at_WM_NOTIFY = 0;
4337 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4338 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4339 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4341 received_WM_NOTIFY = 0;
4342 modify_at_WM_NOTIFY = 0;
4343 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
4344 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4346 received_WM_NOTIFY = 0;
4347 modify_at_WM_NOTIFY = 0;
4348 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4349 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4350 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4352 DestroyWindow(hwndRichedit_WM_NOTIFY);
4353 DestroyWindow(parent);
4356 static void test_undo_coalescing(void)
4358 HWND hwnd;
4359 int result;
4360 char buffer[64] = {0};
4362 /* multi-line control inserts CR normally */
4363 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4364 0, 0, 200, 60, 0, 0, 0, 0);
4365 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4367 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4368 ok (result == FALSE, "Can undo after window creation.\n");
4369 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4370 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
4371 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4372 ok (result == FALSE, "Can redo after window creation.\n");
4373 result = SendMessage(hwnd, EM_REDO, 0, 0);
4374 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
4376 /* Test the effect of arrows keys during typing on undo transactions*/
4377 simulate_typing_characters(hwnd, "one two three");
4378 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
4379 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
4380 simulate_typing_characters(hwnd, " four five six");
4382 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4383 ok (result == FALSE, "Can redo before anything is undone.\n");
4384 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4385 ok (result == TRUE, "Cannot undo typed characters.\n");
4386 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4387 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
4388 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4389 ok (result == TRUE, "Cannot redo after undo.\n");
4390 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4391 result = strcmp(buffer, "one two three");
4392 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4394 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4395 ok (result == TRUE, "Cannot undo typed characters.\n");
4396 result = SendMessage(hwnd, WM_UNDO, 0, 0);
4397 ok (result == TRUE, "Failed to undo typed characters.\n");
4398 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4399 result = strcmp(buffer, "");
4400 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4402 /* Test the effect of focus changes during typing on undo transactions*/
4403 simulate_typing_characters(hwnd, "one two three");
4404 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4405 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
4406 SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
4407 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
4408 simulate_typing_characters(hwnd, " four five six");
4409 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4410 ok (result == TRUE, "Failed to undo typed characters.\n");
4411 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4412 result = strcmp(buffer, "one two three");
4413 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4415 /* Test the effect of the back key during typing on undo transactions */
4416 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4417 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
4418 ok (result == TRUE, "Failed to clear the text.\n");
4419 simulate_typing_characters(hwnd, "one two threa");
4420 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4421 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
4422 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
4423 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
4424 simulate_typing_characters(hwnd, "e four five six");
4425 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4426 ok (result == TRUE, "Failed to undo typed characters.\n");
4427 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4428 result = strcmp(buffer, "");
4429 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4431 /* Test the effect of the delete key during typing on undo transactions */
4432 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4433 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
4434 ok(result == TRUE, "Failed to set the text.\n");
4435 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
4436 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
4437 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
4438 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
4439 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
4440 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4441 ok (result == TRUE, "Failed to undo typed characters.\n");
4442 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4443 result = strcmp(buffer, "acd");
4444 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
4445 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4446 ok (result == TRUE, "Failed to undo typed characters.\n");
4447 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4448 result = strcmp(buffer, "abcd");
4449 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
4451 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
4452 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4453 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
4454 ok (result == TRUE, "Failed to clear the text.\n");
4455 simulate_typing_characters(hwnd, "one two three");
4456 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
4457 ok (result == 0, "expected %d but got %d\n", 0, result);
4458 simulate_typing_characters(hwnd, " four five six");
4459 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4460 ok (result == TRUE, "Failed to undo typed characters.\n");
4461 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4462 result = strcmp(buffer, "one two three");
4463 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4464 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4465 ok (result == TRUE, "Failed to undo typed characters.\n");
4466 ok (result == TRUE, "Failed to undo typed characters.\n");
4467 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4468 result = strcmp(buffer, "");
4469 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4471 DestroyWindow(hwnd);
4474 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
4475 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
4477 static void test_word_movement(void)
4479 HWND hwnd;
4480 int result;
4481 int sel_start, sel_end;
4483 /* multi-line control inserts CR normally */
4484 hwnd = new_richedit(NULL);
4486 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
4487 ok (result == TRUE, "Failed to clear the text.\n");
4488 SendMessage(hwnd, EM_SETSEL, 0, 0);
4489 /* |one two three */
4491 SEND_CTRL_RIGHT(hwnd);
4492 /* one |two three */
4493 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4494 ok(sel_start == sel_end, "Selection should be empty\n");
4495 ok(sel_start == 4, "Cursur is at %d instead of %d\n", sel_start, 4);
4497 SEND_CTRL_RIGHT(hwnd);
4498 /* one two |three */
4499 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4500 ok(sel_start == sel_end, "Selection should be empty\n");
4501 ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4503 SEND_CTRL_LEFT(hwnd);
4504 /* one |two three */
4505 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4506 ok(sel_start == sel_end, "Selection should be empty\n");
4507 ok(sel_start == 4, "Cursur is at %d instead of %d\n", sel_start, 4);
4509 SEND_CTRL_LEFT(hwnd);
4510 /* |one two three */
4511 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4512 ok(sel_start == sel_end, "Selection should be empty\n");
4513 ok(sel_start == 0, "Cursur is at %d instead of %d\n", sel_start, 0);
4515 SendMessage(hwnd, EM_SETSEL, 8, 8);
4516 /* one two | three */
4517 SEND_CTRL_RIGHT(hwnd);
4518 /* one two |three */
4519 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4520 ok(sel_start == sel_end, "Selection should be empty\n");
4521 ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4523 SendMessage(hwnd, EM_SETSEL, 11, 11);
4524 /* one two th|ree */
4525 SEND_CTRL_LEFT(hwnd);
4526 /* one two |three */
4527 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4528 ok(sel_start == sel_end, "Selection should be empty\n");
4529 ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4531 DestroyWindow(hwnd);
4534 static void test_EM_CHARFROMPOS(void)
4536 HWND hwnd;
4537 int result;
4538 POINTL point;
4539 point.x = 0;
4540 point.y = 50;
4542 /* multi-line control inserts CR normally */
4543 hwnd = new_richedit(NULL);
4544 result = SendMessageA(hwnd, WM_SETTEXT, 0,
4545 (LPARAM)"one two three four five six seven");
4547 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
4548 ok(result == 0, "expected character index of 0 but got %d\n", result);
4550 DestroyWindow(hwnd);
4553 START_TEST( editor )
4555 MSG msg;
4556 time_t end;
4558 /* Must explicitly LoadLibrary(). The test has no references to functions in
4559 * RICHED20.DLL, so the linker doesn't actually link to it. */
4560 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
4561 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
4562 test_WM_CHAR();
4563 test_EM_FINDTEXT();
4564 test_EM_GETLINE();
4565 test_EM_POSFROMCHAR();
4566 test_EM_SCROLLCARET();
4567 test_EM_SCROLL();
4568 test_WM_SETTEXT();
4569 test_EM_LINELENGTH();
4570 test_EM_SETCHARFORMAT();
4571 test_EM_SETTEXTMODE();
4572 test_TM_PLAINTEXT();
4573 test_EM_SETOPTIONS();
4574 test_WM_GETTEXT();
4575 test_EM_GETTEXTRANGE();
4576 test_EM_GETSELTEXT();
4577 test_EM_SETUNDOLIMIT();
4578 test_ES_PASSWORD();
4579 test_EM_SETTEXTEX();
4580 test_EM_LIMITTEXT();
4581 test_EM_EXLIMITTEXT();
4582 test_EM_GETLIMITTEXT();
4583 test_WM_SETFONT();
4584 test_EM_GETMODIFY();
4585 test_EM_EXSETSEL();
4586 test_WM_PASTE();
4587 test_EM_STREAMIN();
4588 test_EM_STREAMOUT();
4589 test_EM_StreamIn_Undo();
4590 test_EM_FORMATRANGE();
4591 test_unicode_conversions();
4592 test_EM_GETTEXTLENGTHEX();
4593 test_EM_REPLACESEL(1);
4594 test_EM_REPLACESEL(0);
4595 test_WM_NOTIFY();
4596 test_EM_AUTOURLDETECT();
4597 test_eventMask();
4598 test_undo_coalescing();
4599 test_word_movement();
4600 test_EM_CHARFROMPOS();
4602 /* Set the environment variable WINETEST_RICHED20 to keep windows
4603 * responsive and open for 30 seconds. This is useful for debugging.
4605 * The message pump uses PeekMessage() to empty the queue and then sleeps for
4606 * 50ms before retrying the queue. */
4607 end = time(NULL) + 30;
4608 if (getenv( "WINETEST_RICHED20" )) {
4609 while (time(NULL) < end) {
4610 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
4611 TranslateMessage(&msg);
4612 DispatchMessage(&msg);
4613 } else {
4614 Sleep(50);
4619 OleFlushClipboard();
4620 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());