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
33 #include <wine/test.h>
35 static HMODULE hmoduleRichEdit;
37 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
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());
46 static HWND new_richedit(HWND parent) {
47 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
50 static void processPendingMessages(void)
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);
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)
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);
84 static const char haystack[] = "WINEWine wineWine wine WineWine";
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[] = {
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},
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},
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},
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) {
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,
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,
211 for (i = 0; i < num_tests; i++) {
212 if (find[i]._todo_wine) {
214 check_EM_FINDTEXT(hwnd, name, &find[i], i);
215 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
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);
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);
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,
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 {
271 {0, 10, "foo bar\r"},
276 /* Buffer smaller than line length */
282 static void test_EM_GETLINE(void)
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"
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++)
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,
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),
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");
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);
336 int offset_test[10][2] = {
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)
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);
370 static void move_cursor(HWND hwnd, long charindex)
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)
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 "
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);
439 unsigned int height = 0;
441 static const char text[] = "aa\n"
442 "this is a long line of text that should be longer than the "
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);
477 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
479 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
481 xpos = LOWORD(result);
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);
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));
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));
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);
556 int tested_effects[] = {
570 /* Invalid flags, CHARFORMAT2 structure blanked out */
571 memset(&cf2, 0, sizeof(cf2));
572 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
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,
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,
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,
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,
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,
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,
615 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
616 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
617 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,
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,
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,
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,
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,
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;
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;
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;
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*/
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,
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"*/
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*/
1022 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1024 /*Retrieve its formatting*/
1025 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1028 /*Select a character from the second "wine" string*/
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,
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);
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*/
1067 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1069 /*Retrieve its formatting*/
1070 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1073 /*Select text from the second "wine" string*/
1076 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1078 /*Retrieve its formatting*/
1079 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
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;
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*/
1112 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1113 cf2.cbSize = sizeof(CHARFORMAT2);
1114 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
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);
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*/
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*/
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*/
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};
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);
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);
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);
1234 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
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);
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);
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);
1261 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
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};
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};
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);
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);
1371 { /* control text is url; should get CFE_LINK */
1372 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
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 /* DO NOT change the properties of the first two elements. To shorten the
1387 tests, all tests after WM_SETTEXT test just the first two elements -
1388 one non-URL and one URL */
1394 {"http://www.winehq.org", 1},
1395 {"http//winehq.org", 0},
1396 {"ww.winehq.org", 0},
1397 {"www.winehq.org", 1},
1398 {"ftp://192.168.1.1", 1},
1399 {"ftp//192.168.1.1", 0},
1400 {"mailto:your@email.com", 1},
1401 {"prospero:prosperoserver", 1},
1403 {"news:newserver", 1},
1404 {"wais:waisserver", 1}
1409 HWND hwndRichEdit, parent;
1411 /* All of the following should cause the URL to be detected */
1412 const char * templates_delim[] = {
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\r on it",
1416 "This is some text with ---X--- on it",
1417 "This is some text with \"X\" on it",
1418 "This is some text with 'X' on it",
1419 "This is some text with 'X' on it",
1420 "This is some text with :X: on it",
1422 "This text ends with X",
1424 "This is some text with X) on it",
1425 "This is some text with X--- on it",
1426 "This is some text with X\" on it",
1427 "This is some text with X' on it",
1428 "This is some text with X: on it",
1430 "This is some text with (X on it",
1431 "This is some text with \rX on it",
1432 "This is some text with ---X on it",
1433 "This is some text with \"X on it",
1434 "This is some text with 'X on it",
1435 "This is some text with :X on it",
1437 /* None of these should cause the URL to be detected */
1438 const char * templates_non_delim[] = {
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",
1452 "This is some text with #X on it",
1453 "This is some text with @X on it",
1454 "This is some text with \\X on it",
1456 /* All of these cause the URL detection to be extended by one more byte,
1457 thus demonstrating that the tested character is considered as part
1459 const char * templates_xten_delim[] = {
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",
1465 "This is some text with X# on it",
1466 "This is some text with X@ on it",
1467 "This is some text with X\\ on it",
1471 parent = new_static_wnd(NULL);
1472 hwndRichEdit = new_richedit(parent);
1473 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1474 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1475 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1476 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1477 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1478 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1479 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1480 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1481 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1482 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1483 /* for each url, check the text to see if CFE_LINK effect is present */
1484 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1486 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1487 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1488 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1490 /* Link detection should happen immediately upon WM_SETTEXT */
1491 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1492 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1493 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1495 DestroyWindow(hwndRichEdit);
1497 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1498 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1499 hwndRichEdit = new_richedit(parent);
1501 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1506 at_pos = strchr(templates_delim[j], 'X');
1507 at_offset = at_pos - templates_delim[j];
1508 strncpy(buffer, templates_delim[j], at_offset);
1509 buffer[at_offset] = '\0';
1510 strcat(buffer, urls[i].text);
1511 strcat(buffer, templates_delim[j] + at_offset + 1);
1512 end_offset = at_offset + strlen(urls[i].text);
1514 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1515 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1517 /* This assumes no templates start with the URL itself, and that they
1518 have at least two characters before the URL text */
1519 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1520 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1521 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1522 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1523 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1524 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1528 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1529 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1530 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1531 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1535 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1536 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1537 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1538 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1540 if (buffer[end_offset] != '\0')
1542 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1543 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1544 if (buffer[end_offset +1] != '\0')
1546 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1547 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1552 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1557 at_pos = strchr(templates_non_delim[j], 'X');
1558 at_offset = at_pos - templates_non_delim[j];
1559 strncpy(buffer, templates_non_delim[j], at_offset);
1560 buffer[at_offset] = '\0';
1561 strcat(buffer, urls[i].text);
1562 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1563 end_offset = at_offset + strlen(urls[i].text);
1565 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1566 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1568 /* This assumes no templates start with the URL itself, and that they
1569 have at least two characters before the URL text */
1570 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1571 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1572 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1573 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1574 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1575 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1577 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1578 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1579 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1580 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1581 if (buffer[end_offset] != '\0')
1583 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1584 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1585 if (buffer[end_offset +1] != '\0')
1587 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1588 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1593 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1598 at_pos = strchr(templates_xten_delim[j], 'X');
1599 at_offset = at_pos - templates_xten_delim[j];
1600 strncpy(buffer, templates_xten_delim[j], at_offset);
1601 buffer[at_offset] = '\0';
1602 strcat(buffer, urls[i].text);
1603 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1604 end_offset = at_offset + strlen(urls[i].text);
1606 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1607 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1609 /* This assumes no templates start with the URL itself, and that they
1610 have at least two characters before the URL text */
1611 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1612 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1613 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1614 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1615 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1616 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1620 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1621 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1622 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1623 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1624 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1625 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1629 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1630 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1631 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1632 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1633 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1634 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1636 if (buffer[end_offset +1] != '\0')
1638 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1639 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1640 if (buffer[end_offset +2] != '\0')
1642 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1643 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1648 DestroyWindow(hwndRichEdit);
1649 hwndRichEdit = NULL;
1652 /* Test detection of URLs within normal text - WM_CHAR case. */
1653 /* Test only the first two URL examples for brevity */
1654 for (i = 0; i < 2; i++) {
1655 hwndRichEdit = new_richedit(parent);
1657 /* Also for brevity, test only the first three delimiters */
1658 for (j = 0; j < 3; j++) {
1664 at_pos = strchr(templates_delim[j], 'X');
1665 at_offset = at_pos - templates_delim[j];
1666 end_offset = at_offset + strlen(urls[i].text);
1668 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1669 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1670 for (u = 0; templates_delim[j][u]; u++) {
1671 if (templates_delim[j][u] == '\r') {
1672 simulate_typing_characters(hwndRichEdit, "\r");
1673 } else if (templates_delim[j][u] != 'X') {
1674 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1676 for (v = 0; urls[i].text[v]; v++) {
1677 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1681 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1683 /* This assumes no templates start with the URL itself, and that they
1684 have at least two characters before the URL text */
1685 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1686 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1687 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1688 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1689 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1690 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1694 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1695 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1696 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1697 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1701 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1702 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1703 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1704 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1706 if (buffer[end_offset] != '\0')
1708 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1710 if (buffer[end_offset +1] != '\0')
1712 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1713 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1717 /* The following will insert a paragraph break after the first character
1718 of the URL candidate, thus breaking the URL. It is expected that the
1719 CFE_LINK attribute should break across both pieces of the URL */
1720 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1721 simulate_typing_characters(hwndRichEdit, "\r");
1722 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1724 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1725 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1726 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1727 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1728 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1729 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1731 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1732 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1733 /* end_offset moved because of paragraph break */
1734 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1735 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1736 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1737 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1739 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1740 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1741 if (buffer[end_offset +2] != '\0')
1743 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1744 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1748 /* The following will remove the just-inserted paragraph break, thus
1749 restoring the URL */
1750 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1751 simulate_typing_characters(hwndRichEdit, "\b");
1752 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1754 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1755 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1756 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1757 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1758 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1759 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1763 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1764 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1765 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1766 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1770 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1771 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1772 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1773 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1775 if (buffer[end_offset] != '\0')
1777 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1778 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1779 if (buffer[end_offset +1] != '\0')
1781 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1782 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1786 DestroyWindow(hwndRichEdit);
1787 hwndRichEdit = NULL;
1790 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1791 /* Test just the first two URL examples for brevity */
1792 for (i = 0; i < 2; i++) {
1795 hwndRichEdit = new_richedit(parent);
1797 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1799 1) Set entire text, a la WM_SETTEXT
1800 2) Set a selection of the text to the URL
1801 3) Set a portion of the text at a time, which eventually results in
1803 All of them should give equivalent results
1806 /* Set entire text in one go, like WM_SETTEXT */
1807 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1812 st.codepage = CP_ACP;
1813 st.flags = ST_DEFAULT;
1815 at_pos = strchr(templates_delim[j], 'X');
1816 at_offset = at_pos - templates_delim[j];
1817 strncpy(buffer, templates_delim[j], at_offset);
1818 buffer[at_offset] = '\0';
1819 strcat(buffer, urls[i].text);
1820 strcat(buffer, templates_delim[j] + at_offset + 1);
1821 end_offset = at_offset + strlen(urls[i].text);
1823 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1824 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1826 /* This assumes no templates start with the URL itself, and that they
1827 have at least two characters before the URL text */
1828 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1829 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1830 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1831 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1832 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1833 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1837 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1838 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1839 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1840 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1844 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1845 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1846 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1847 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1849 if (buffer[end_offset] != '\0')
1851 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1852 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1853 if (buffer[end_offset +1] != '\0')
1855 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1856 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1861 /* Set selection with X to the URL */
1862 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1867 at_pos = strchr(templates_delim[j], 'X');
1868 at_offset = at_pos - templates_delim[j];
1869 end_offset = at_offset + strlen(urls[i].text);
1871 st.codepage = CP_ACP;
1872 st.flags = ST_DEFAULT;
1873 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1874 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1875 st.flags = ST_SELECTION;
1876 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1877 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1878 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1880 /* This assumes no templates start with the URL itself, and that they
1881 have at least two characters before the URL text */
1882 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1883 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1884 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1885 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1886 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1887 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1891 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1892 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1893 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1894 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1898 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1899 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1900 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1901 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1903 if (buffer[end_offset] != '\0')
1905 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1906 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1907 if (buffer[end_offset +1] != '\0')
1909 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1910 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1915 /* Set selection with X to the first character of the URL, then the rest */
1916 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1921 at_pos = strchr(templates_delim[j], 'X');
1922 at_offset = at_pos - templates_delim[j];
1923 end_offset = at_offset + strlen(urls[i].text);
1925 strcpy(buffer, "YY");
1926 buffer[0] = urls[i].text[0];
1928 st.codepage = CP_ACP;
1929 st.flags = ST_DEFAULT;
1930 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1931 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1932 st.flags = ST_SELECTION;
1933 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1934 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1935 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1936 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1937 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1939 /* This assumes no templates start with the URL itself, and that they
1940 have at least two characters before the URL text */
1941 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1942 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1943 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1944 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1945 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1946 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1950 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1951 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1952 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1953 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1957 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1958 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1959 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1960 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1962 if (buffer[end_offset] != '\0')
1964 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1965 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1966 if (buffer[end_offset +1] != '\0')
1968 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1969 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1974 DestroyWindow(hwndRichEdit);
1975 hwndRichEdit = NULL;
1978 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
1979 /* Test just the first two URL examples for brevity */
1980 for (i = 0; i < 2; i++) {
1981 hwndRichEdit = new_richedit(parent);
1983 /* Set selection with X to the URL */
1984 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1989 at_pos = strchr(templates_delim[j], 'X');
1990 at_offset = at_pos - templates_delim[j];
1991 end_offset = at_offset + strlen(urls[i].text);
1993 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1994 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
1995 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1996 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
1997 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1999 /* This assumes no templates start with the URL itself, and that they
2000 have at least two characters before the URL text */
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2003 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2004 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2005 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2006 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2010 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2011 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2012 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2013 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2017 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2018 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2019 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2020 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2022 if (buffer[end_offset] != '\0')
2024 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2025 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2026 if (buffer[end_offset +1] != '\0')
2028 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2029 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2034 /* Set selection with X to the first character of the URL, then the rest */
2035 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2040 at_pos = strchr(templates_delim[j], 'X');
2041 at_offset = at_pos - templates_delim[j];
2042 end_offset = at_offset + strlen(urls[i].text);
2044 strcpy(buffer, "YY");
2045 buffer[0] = urls[i].text[0];
2047 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2048 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2049 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2050 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2051 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2052 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2053 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2055 /* This assumes no templates start with the URL itself, and that they
2056 have at least two characters before the URL text */
2057 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2058 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2059 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2060 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2061 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2062 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2066 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2067 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2068 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2069 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2073 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2074 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2075 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2076 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2078 if (buffer[end_offset] != '\0')
2080 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2081 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2082 if (buffer[end_offset +1] != '\0')
2084 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2085 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2090 DestroyWindow(hwndRichEdit);
2091 hwndRichEdit = NULL;
2094 DestroyWindow(parent);
2097 static void test_EM_SCROLL(void)
2100 int r; /* return value */
2101 int expr; /* expected return value */
2102 HWND hwndRichEdit = new_richedit(NULL);
2103 int y_before, y_after; /* units of lines of text */
2105 /* test a richedit box containing a single line of text */
2106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2108 for (i = 0; i < 4; i++) {
2109 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2111 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2112 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2113 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2114 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2115 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2116 "(i == %d)\n", y_after, i);
2120 * test a richedit box that will scroll. There are two general
2121 * cases: the case without any long lines and the case with a long
2124 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2126 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2128 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2129 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2130 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2131 "LONG LINE \nb\nc\nd\ne");
2132 for (j = 0; j < 12; j++) /* reset scroll position to top */
2133 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2135 /* get first visible line */
2136 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2137 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2139 /* get new current first visible line */
2140 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2142 ok(((r & 0xffffff00) == 0x00010000) &&
2143 ((r & 0x000000ff) != 0x00000000),
2144 "EM_SCROLL page down didn't scroll by a small positive number of "
2145 "lines (r == 0x%08x)\n", r);
2146 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2147 "(line %d scrolled to line %d\n", y_before, y_after);
2151 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2152 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2153 ok(((r & 0xffffff00) == 0x0001ff00),
2154 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2155 "(r == 0x%08x)\n", r);
2156 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2157 "%d scrolled to line %d\n", y_before, y_after);
2161 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2163 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2165 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2166 "(r == 0x%08x)\n", r);
2167 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2168 "1 line (%d scrolled to %d)\n", y_before, y_after);
2172 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2174 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2176 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2177 "(r == 0x%08x)\n", r);
2178 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2179 "line (%d scrolled to %d)\n", y_before, y_after);
2183 r = SendMessage(hwndRichEdit, EM_SCROLL,
2184 SB_LINEUP, 0); /* lineup beyond top */
2186 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2189 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2190 ok(y_before == y_after,
2191 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2195 r = SendMessage(hwndRichEdit, EM_SCROLL,
2196 SB_PAGEUP, 0);/*page up beyond top */
2198 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2201 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2202 ok(y_before == y_after,
2203 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2205 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2206 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2207 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2208 r = SendMessage(hwndRichEdit, EM_SCROLL,
2209 SB_PAGEDOWN, 0); /* page down beyond bot */
2210 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2213 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2214 ok(y_before == y_after,
2215 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2218 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2219 SendMessage(hwndRichEdit, EM_SCROLL,
2220 SB_LINEDOWN, 0); /* line down beyond bot */
2221 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2224 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2225 ok(y_before == y_after,
2226 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2229 DestroyWindow(hwndRichEdit);
2232 static void test_EM_SETUNDOLIMIT(void)
2234 /* cases we test for:
2235 * default behaviour - limiting at 100 undo's
2236 * undo disabled - setting a limit of 0
2237 * undo limited - undo limit set to some to some number, like 2
2238 * bad input - sending a negative number should default to 100 undo's */
2240 HWND hwndRichEdit = new_richedit(NULL);
2245 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2248 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2249 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2250 also, multiple pastes don't combine like WM_CHAR would */
2251 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2253 /* first case - check the default */
2254 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2255 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2256 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2257 for (i=0; i<100; i++) /* Undo 100 of them */
2258 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2259 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2260 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
2262 /* second case - cannot undo */
2263 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2264 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
2265 SendMessage(hwndRichEdit,
2266 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
2267 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2268 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
2270 /* third case - set it to an arbitrary number */
2271 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2272 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
2273 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2274 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2275 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2276 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
2277 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
2278 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
2279 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2280 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2281 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
2282 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2283 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2284 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
2286 /* fourth case - setting negative numbers should default to 100 undos */
2287 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2288 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
2290 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
2292 DestroyWindow(hwndRichEdit);
2295 static void test_ES_PASSWORD(void)
2297 /* This isn't hugely testable, so we're just going to run it through its paces */
2299 HWND hwndRichEdit = new_richedit(NULL);
2302 /* First, check the default of a regular control */
2303 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2305 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
2307 /* Now, set it to something normal */
2308 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
2309 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2311 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2313 /* Now, set it to something odd */
2314 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
2315 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2317 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2318 DestroyWindow(hwndRichEdit);
2321 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
2326 char** str = (char**)dwCookie;
2329 memcpy(*str, pbBuff, *pcb);
2335 static void test_WM_SETTEXT()
2337 HWND hwndRichEdit = new_richedit(NULL);
2338 const char * TestItem1 = "TestSomeText";
2339 const char * TestItem2 = "TestSomeText\r";
2340 const char * TestItem2_after = "TestSomeText\r\n";
2341 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
2342 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
2343 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
2344 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
2345 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
2346 const char * TestItem5_after = "TestSomeText TestSomeText";
2347 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
2348 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
2349 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
2350 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
2352 char buf[1024] = {0};
2357 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
2358 any solitary \r to be converted to \r\n on return. Properly paired
2359 \r\n are not affected. It also shows that the special sequence \r\r\n
2360 gets converted to a single space.
2363 #define TEST_SETTEXT(a, b) \
2364 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
2365 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
2366 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
2367 ok (result == lstrlen(buf), \
2368 "WM_GETTEXT returned %ld instead of expected %u\n", \
2369 result, lstrlen(buf)); \
2370 result = strcmp(b, buf); \
2372 "WM_SETTEXT round trip: strcmp = %ld\n", result);
2374 TEST_SETTEXT(TestItem1, TestItem1)
2375 TEST_SETTEXT(TestItem2, TestItem2_after)
2376 TEST_SETTEXT(TestItem3, TestItem3_after)
2377 TEST_SETTEXT(TestItem3_after, TestItem3_after)
2378 TEST_SETTEXT(TestItem4, TestItem4_after)
2379 TEST_SETTEXT(TestItem5, TestItem5_after)
2380 TEST_SETTEXT(TestItem6, TestItem6_after)
2381 TEST_SETTEXT(TestItem7, TestItem7_after)
2383 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
2384 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2386 es.dwCookie = (DWORD_PTR)&p;
2388 es.pfnCallback = test_WM_SETTEXT_esCallback;
2389 memset(buf, 0, sizeof(buf));
2390 SendMessage(hwndRichEdit, EM_STREAMOUT,
2391 (WPARAM)(SF_RTF), (LPARAM)&es);
2392 trace("EM_STREAMOUT produced: \n%s\n", buf);
2393 TEST_SETTEXT(buf, TestItem1)
2396 DestroyWindow(hwndRichEdit);
2399 static void test_EM_STREAMOUT(void)
2401 HWND hwndRichEdit = new_richedit(NULL);
2404 char buf[1024] = {0};
2407 const char * TestItem1 = "TestSomeText";
2408 const char * TestItem2 = "TestSomeText\r";
2409 const char * TestItem3 = "TestSomeText\r\n";
2411 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2413 es.dwCookie = (DWORD_PTR)&p;
2415 es.pfnCallback = test_WM_SETTEXT_esCallback;
2416 memset(buf, 0, sizeof(buf));
2417 SendMessage(hwndRichEdit, EM_STREAMOUT,
2418 (WPARAM)(SF_TEXT), (LPARAM)&es);
2420 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
2421 ok(strcmp(buf, TestItem1) == 0,
2422 "streamed text different, got %s\n", buf);
2424 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
2426 es.dwCookie = (DWORD_PTR)&p;
2428 es.pfnCallback = test_WM_SETTEXT_esCallback;
2429 memset(buf, 0, sizeof(buf));
2430 SendMessage(hwndRichEdit, EM_STREAMOUT,
2431 (WPARAM)(SF_TEXT), (LPARAM)&es);
2433 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
2434 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2435 ok(strcmp(buf, TestItem3) == 0,
2436 "streamed text different from, got %s\n", buf);
2437 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
2439 es.dwCookie = (DWORD_PTR)&p;
2441 es.pfnCallback = test_WM_SETTEXT_esCallback;
2442 memset(buf, 0, sizeof(buf));
2443 SendMessage(hwndRichEdit, EM_STREAMOUT,
2444 (WPARAM)(SF_TEXT), (LPARAM)&es);
2446 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2447 ok(strcmp(buf, TestItem3) == 0,
2448 "streamed text different, got %s\n", buf);
2450 DestroyWindow(hwndRichEdit);
2453 static void test_EM_SETTEXTEX(void)
2455 HWND hwndRichEdit = new_richedit(NULL);
2458 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2460 'T', 'e', 'x', 't', 0};
2461 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2465 const char * TestItem2_after = "TestSomeText\r\n";
2466 WCHAR TestItem3[] = {'T', 'e', 's', 't',
2469 '\r','\n','\r','\n', 0};
2470 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
2474 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
2478 WCHAR TestItem4[] = {'T', 'e', 's', 't',
2481 '\r','\r','\n','\r',
2483 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
2487 #define MAX_BUF_LEN 1024
2488 WCHAR buf[MAX_BUF_LEN];
2494 setText.codepage = 1200; /* no constant for unicode */
2495 getText.codepage = 1200; /* no constant for unicode */
2496 getText.cb = MAX_BUF_LEN;
2497 getText.flags = GT_DEFAULT;
2498 getText.lpDefaultChar = NULL;
2499 getText.lpUsedDefChar = NULL;
2502 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2503 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2504 ok(lstrcmpW(buf, TestItem1) == 0,
2505 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2507 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
2508 convert \r to \r\n on return
2510 setText.codepage = 1200; /* no constant for unicode */
2511 getText.codepage = 1200; /* no constant for unicode */
2512 getText.cb = MAX_BUF_LEN;
2513 getText.flags = GT_DEFAULT;
2514 getText.lpDefaultChar = NULL;
2515 getText.lpUsedDefChar = NULL;
2517 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
2518 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2519 ok(lstrcmpW(buf, TestItem2) == 0,
2520 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2522 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
2523 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
2524 ok(strcmp((const char *)buf, TestItem2_after) == 0,
2525 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
2527 /* Baseline test for just-enough buffer space for string */
2528 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2529 getText.codepage = 1200; /* no constant for unicode */
2530 getText.flags = GT_DEFAULT;
2531 getText.lpDefaultChar = NULL;
2532 getText.lpUsedDefChar = NULL;
2533 memset(buf, 0, MAX_BUF_LEN);
2534 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2535 ok(lstrcmpW(buf, TestItem2) == 0,
2536 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2538 /* When there is enough space for one character, but not both, of the CRLF
2539 pair at the end of the string, the CR is not copied at all. That is,
2540 the caller must not see CRLF pairs truncated to CR at the end of the
2543 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2544 getText.codepage = 1200; /* no constant for unicode */
2545 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
2546 getText.lpDefaultChar = NULL;
2547 getText.lpUsedDefChar = NULL;
2548 memset(buf, 0, MAX_BUF_LEN);
2549 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2550 ok(lstrcmpW(buf, TestItem1) == 0,
2551 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2554 /* \r\n pairs get changed into \r */
2555 setText.codepage = 1200; /* no constant for unicode */
2556 getText.codepage = 1200; /* no constant for unicode */
2557 getText.cb = MAX_BUF_LEN;
2558 getText.flags = GT_DEFAULT;
2559 getText.lpDefaultChar = NULL;
2560 getText.lpUsedDefChar = NULL;
2562 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
2563 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2564 ok(lstrcmpW(buf, TestItem3_after) == 0,
2565 "EM_SETTEXTEX did not convert properly\n");
2567 /* \n also gets changed to \r */
2568 setText.codepage = 1200; /* no constant for unicode */
2569 getText.codepage = 1200; /* no constant for unicode */
2570 getText.cb = MAX_BUF_LEN;
2571 getText.flags = GT_DEFAULT;
2572 getText.lpDefaultChar = NULL;
2573 getText.lpUsedDefChar = NULL;
2575 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
2576 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2577 ok(lstrcmpW(buf, TestItem3_after) == 0,
2578 "EM_SETTEXTEX did not convert properly\n");
2580 /* \r\r\n gets changed into single space */
2581 setText.codepage = 1200; /* no constant for unicode */
2582 getText.codepage = 1200; /* no constant for unicode */
2583 getText.cb = MAX_BUF_LEN;
2584 getText.flags = GT_DEFAULT;
2585 getText.lpDefaultChar = NULL;
2586 getText.lpUsedDefChar = NULL;
2588 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
2589 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2590 ok(lstrcmpW(buf, TestItem4_after) == 0,
2591 "EM_SETTEXTEX did not convert properly\n");
2593 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2594 (WPARAM)&setText, (LPARAM) NULL);
2595 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2598 "EM_SETTEXTEX returned %d, instead of 1\n",result);
2599 ok(lstrlenW(buf) == 0,
2600 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
2602 /* put some text back */
2604 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2605 /* select some text */
2608 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2609 /* replace current selection */
2610 setText.flags = ST_SELECTION;
2611 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2612 (WPARAM)&setText, (LPARAM) NULL);
2614 "EM_SETTEXTEX with NULL lParam to replace selection"
2615 " with no text should return 0. Got %i\n",
2618 /* put some text back */
2620 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2621 /* select some text */
2624 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2625 /* replace current selection */
2626 setText.flags = ST_SELECTION;
2627 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2628 (WPARAM)&setText, (LPARAM) TestItem1);
2630 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2631 ok(result == lstrlenW(TestItem1),
2632 "EM_SETTEXTEX with NULL lParam to replace selection"
2633 " with no text should return 0. Got %i\n",
2635 ok(lstrlenW(buf) == 22,
2636 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
2639 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
2640 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
2642 es.dwCookie = (DWORD_PTR)&p;
2644 es.pfnCallback = test_WM_SETTEXT_esCallback;
2645 memset(buf, 0, sizeof(buf));
2646 SendMessage(hwndRichEdit, EM_STREAMOUT,
2647 (WPARAM)(SF_RTF), (LPARAM)&es);
2648 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
2650 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
2651 getText.codepage = 1200; /* no constant for unicode */
2652 getText.cb = MAX_BUF_LEN;
2653 getText.flags = GT_DEFAULT;
2654 getText.lpDefaultChar = NULL;
2655 getText.lpUsedDefChar = NULL;
2658 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
2659 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2660 ok(lstrcmpW(buf, TestItem1) == 0,
2661 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2664 DestroyWindow(hwndRichEdit);
2667 static void test_EM_LIMITTEXT(void)
2671 HWND hwndRichEdit = new_richedit(NULL);
2673 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
2674 * about setting the length to -1 for multiline edit controls doesn't happen.
2677 /* Don't check default gettextlimit case. That's done in other tests */
2679 /* Set textlimit to 100 */
2680 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
2681 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2683 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
2685 /* Set textlimit to 0 */
2686 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
2687 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2689 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
2691 /* Set textlimit to -1 */
2692 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
2693 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2695 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
2697 /* Set textlimit to -2 */
2698 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
2699 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2701 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
2703 DestroyWindow (hwndRichEdit);
2707 static void test_EM_EXLIMITTEXT(void)
2709 int i, selBegin, selEnd, len1, len2;
2711 char text[1024 + 1];
2712 char buffer[1024 + 1];
2713 int textlimit = 0; /* multiple of 100 */
2714 HWND hwndRichEdit = new_richedit(NULL);
2716 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2717 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
2720 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2721 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2723 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2726 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2727 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2729 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2731 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
2732 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2733 /* default for WParam = 0 */
2734 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
2736 textlimit = sizeof(text)-1;
2737 memset(text, 'W', textlimit);
2738 text[sizeof(text)-1] = 0;
2739 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2740 /* maxed out text */
2741 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2743 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2744 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2745 len1 = selEnd - selBegin;
2747 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
2748 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
2749 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
2750 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2751 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2752 len2 = selEnd - selBegin;
2755 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2758 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2759 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2760 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
2761 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2762 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2763 len1 = selEnd - selBegin;
2766 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2769 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2770 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2771 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
2772 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2773 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2774 len2 = selEnd - selBegin;
2777 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2780 /* set text up to the limit, select all the text, then add a char */
2782 memset(text, 'W', textlimit);
2783 text[textlimit] = 0;
2784 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2785 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2786 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2787 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2788 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2789 result = strcmp(buffer, "A");
2790 ok(0 == result, "got string = \"%s\"\n", buffer);
2792 /* WM_SETTEXT not limited */
2794 memset(text, 'W', textlimit);
2795 text[textlimit] = 0;
2796 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
2797 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2798 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2800 ok(10 == i, "expected 10 chars\n");
2801 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2802 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2804 /* try inserting more text at end */
2805 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2806 ok(0 == i, "WM_CHAR wasn't processed\n");
2807 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2809 ok(10 == i, "expected 10 chars, got %i\n", i);
2810 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2811 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2813 /* try inserting text at beginning */
2814 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
2815 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2816 ok(0 == i, "WM_CHAR wasn't processed\n");
2817 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2819 ok(10 == i, "expected 10 chars, got %i\n", i);
2820 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2821 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2823 /* WM_CHAR is limited */
2825 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2826 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2827 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2828 ok(0 == i, "WM_CHAR wasn't processed\n");
2829 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2830 ok(0 == i, "WM_CHAR wasn't processed\n");
2831 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2833 ok(1 == i, "expected 1 chars, got %i instead\n", i);
2835 DestroyWindow(hwndRichEdit);
2838 static void test_EM_GETLIMITTEXT(void)
2841 HWND hwndRichEdit = new_richedit(NULL);
2843 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2844 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
2846 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
2847 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2848 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
2850 DestroyWindow(hwndRichEdit);
2853 static void test_WM_SETFONT(void)
2855 /* There is no invalid input or error conditions for this function.
2856 * NULL wParam and lParam just fall back to their default values
2857 * It should be noted that even if you use a gibberish name for your fonts
2858 * here, it will still work because the name is stored. They will display as
2859 * System, but will report their name to be whatever they were created as */
2861 HWND hwndRichEdit = new_richedit(NULL);
2862 HFONT testFont1 = 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, "Marlett");
2865 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2866 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2867 FF_DONTCARE, "MS Sans Serif");
2868 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2869 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2870 FF_DONTCARE, "Courier");
2871 LOGFONTA sentLogFont;
2872 CHARFORMAT2A returnedCF2A;
2874 returnedCF2A.cbSize = sizeof(returnedCF2A);
2876 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2877 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
2878 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2880 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
2881 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2882 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
2883 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2885 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
2886 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2887 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
2888 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2889 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
2890 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2892 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
2893 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2894 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
2895 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2896 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
2897 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2899 /* This last test is special since we send in NULL. We clear the variables
2900 * and just compare to "System" instead of the sent in font name. */
2901 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
2902 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
2903 returnedCF2A.cbSize = sizeof(returnedCF2A);
2905 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
2906 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2907 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
2908 ok (!strcmp("System",returnedCF2A.szFaceName),
2909 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
2911 DestroyWindow(hwndRichEdit);
2915 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
2920 const char** str = (const char**)dwCookie;
2921 int size = strlen(*str);
2922 if(size > 3) /* let's make it piecemeal for fun */
2929 memcpy(pbBuff, *str, *pcb);
2935 static void test_EM_GETMODIFY(void)
2937 HWND hwndRichEdit = new_richedit(NULL);
2940 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2942 'T', 'e', 'x', 't', 0};
2943 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2945 'O', 't', 'h', 'e', 'r',
2946 'T', 'e', 'x', 't', 0};
2947 const char* streamText = "hello world";
2952 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2953 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2954 FF_DONTCARE, "Courier");
2956 setText.codepage = 1200; /* no constant for unicode */
2957 setText.flags = ST_KEEPUNDO;
2960 /* modify flag shouldn't be set when richedit is first created */
2961 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2963 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
2965 /* setting modify flag should actually set it */
2966 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
2967 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2969 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
2971 /* clearing modify flag should actually clear it */
2972 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2973 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2975 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
2977 /* setting font doesn't change modify flag */
2978 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2979 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
2980 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2982 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
2984 /* setting text should set modify flag */
2985 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2986 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2987 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2989 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
2991 /* undo previous text doesn't reset modify flag */
2992 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2993 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2995 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
2997 /* set text with no flag to keep undo stack should not set modify flag */
2998 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3000 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3001 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3003 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
3005 /* WM_SETTEXT doesn't modify */
3006 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3007 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3008 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3010 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3012 /* clear the text */
3013 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3014 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3015 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3017 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3020 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3021 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3022 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3023 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3024 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3026 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3028 /* copy/paste text 1 */
3029 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3030 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3031 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3032 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3033 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3035 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3037 /* copy/paste text 2 */
3038 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3039 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3040 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3041 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3042 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3043 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3045 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3048 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3049 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3050 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3051 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3053 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3056 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3057 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3058 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
3059 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3061 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3063 /* set char format */
3064 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3065 cf2.cbSize = sizeof(CHARFORMAT2);
3066 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3068 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3069 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3070 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3071 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3072 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3073 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3075 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3077 /* set para format */
3078 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3079 pf2.cbSize = sizeof(PARAFORMAT2);
3080 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3082 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3083 pf2.wAlignment = PFA_RIGHT;
3084 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3085 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3087 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3090 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3091 es.dwCookie = (DWORD_PTR)&streamText;
3093 es.pfnCallback = test_EM_GETMODIFY_esCallback;
3094 SendMessage(hwndRichEdit, EM_STREAMIN,
3095 (WPARAM)(SF_TEXT), (LPARAM)&es);
3096 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3098 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
3100 DestroyWindow(hwndRichEdit);
3106 long expected_retval;
3107 int expected_getsel_start;
3108 int expected_getsel_end;
3109 int _exsetsel_todo_wine;
3110 int _getsel_todo_wine;
3113 const struct exsetsel_s exsetsel_tests[] = {
3115 {5, 10, 10, 5, 10, 0, 0},
3116 {15, 17, 17, 15, 17, 0, 0},
3117 /* test cpMax > strlen() */
3118 {0, 100, 18, 0, 18, 0, 1},
3119 /* test cpMin == cpMax */
3120 {5, 5, 5, 5, 5, 0, 0},
3121 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
3122 {-1, 0, 5, 5, 5, 0, 0},
3123 {-1, 17, 5, 5, 5, 0, 0},
3124 {-1, 18, 5, 5, 5, 0, 0},
3125 /* test cpMin < 0 && cpMax < 0 */
3126 {-1, -1, 17, 17, 17, 0, 0},
3127 {-4, -5, 17, 17, 17, 0, 0},
3128 /* test cMin >=0 && cpMax < 0 (bug 6814) */
3129 {0, -1, 18, 0, 18, 0, 1},
3130 {17, -5, 18, 17, 18, 0, 1},
3131 {18, -3, 17, 17, 17, 0, 0},
3132 /* test if cpMin > cpMax */
3133 {15, 19, 18, 15, 18, 0, 1},
3134 {19, 15, 18, 15, 18, 0, 1}
3137 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
3142 cr.cpMin = setsel->min;
3143 cr.cpMax = setsel->max;
3144 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
3146 if (setsel->_exsetsel_todo_wine) {
3148 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3151 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3154 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
3156 if (setsel->_getsel_todo_wine) {
3158 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);
3161 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);
3165 static void test_EM_EXSETSEL(void)
3167 HWND hwndRichEdit = new_richedit(NULL);
3169 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
3171 /* sending some text to the window */
3172 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3173 /* 01234567890123456*/
3176 for (i = 0; i < num_tests; i++) {
3177 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
3180 DestroyWindow(hwndRichEdit);
3183 static void test_EM_REPLACESEL(int redraw)
3185 HWND hwndRichEdit = new_richedit(NULL);
3186 char buffer[1024] = {0};
3191 /* sending some text to the window */
3192 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3193 /* 01234567890123456*/
3196 /* FIXME add more tests */
3197 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
3198 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
3199 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
3200 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3201 r = strcmp(buffer, "testing");
3202 ok(0 == r, "expected %d, got %d\n", 0, r);
3204 DestroyWindow(hwndRichEdit);
3206 hwndRichEdit = new_richedit(NULL);
3208 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
3209 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
3211 /* Test behavior with carriage returns and newlines */
3212 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3213 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
3214 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
3215 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3216 r = strcmp(buffer, "RichEdit1");
3217 ok(0 == r, "expected %d, got %d\n", 0, r);
3219 getText.codepage = CP_ACP;
3220 getText.flags = GT_DEFAULT;
3221 getText.lpDefaultChar = NULL;
3222 getText.lpUsedDefChar = NULL;
3223 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3224 ok(strcmp(buffer, "RichEdit1") == 0,
3225 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
3227 /* Test number of lines reported after EM_REPLACESEL */
3228 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3229 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3231 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3232 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
3233 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
3234 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3235 r = strcmp(buffer, "RichEdit1\r\n");
3236 ok(0 == r, "expected %d, got %d\n", 0, r);
3238 getText.codepage = CP_ACP;
3239 getText.flags = GT_DEFAULT;
3240 getText.lpDefaultChar = NULL;
3241 getText.lpUsedDefChar = NULL;
3242 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3243 ok(strcmp(buffer, "RichEdit1\r") == 0,
3244 "EM_GETTEXTEX returned incorrect string\n");
3246 /* Test number of lines reported after EM_REPLACESEL */
3247 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3248 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3250 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
3251 EM_REPLACESEL. The general rule seems to be that Win98's riched20
3252 returns the number of characters *inserted* into the control (after
3253 required conversions), but WinXP's riched20 returns the number of
3254 characters interpreted from the original lParam. Wine's builtin riched20
3255 implements the WinXP behavior.
3257 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3258 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
3259 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
3260 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
3262 /* Test number of lines reported after EM_REPLACESEL */
3263 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3264 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3266 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3267 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3268 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3269 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3271 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3272 r = strcmp(buffer, "RichEdit1\r\n");
3273 ok(0 == r, "expected %d, got %d\n", 0, r);
3275 getText.codepage = CP_ACP;
3276 getText.flags = GT_DEFAULT;
3277 getText.lpDefaultChar = NULL;
3278 getText.lpUsedDefChar = NULL;
3279 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3280 ok(strcmp(buffer, "RichEdit1\r") == 0,
3281 "EM_GETTEXTEX returned incorrect string\n");
3283 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3284 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3285 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3286 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3288 /* The following tests show that richedit should handle the special \r\r\n
3289 sequence by turning it into a single space on insertion. However,
3290 EM_REPLACESEL on WinXP returns the number of characters in the original
3294 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3295 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
3296 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
3297 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3298 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3299 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3300 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3302 /* Test the actual string */
3304 getText.codepage = CP_ACP;
3305 getText.flags = GT_DEFAULT;
3306 getText.lpDefaultChar = NULL;
3307 getText.lpUsedDefChar = NULL;
3308 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3309 ok(strcmp(buffer, "\r\r") == 0,
3310 "EM_GETTEXTEX returned incorrect string\n");
3312 /* Test number of lines reported after EM_REPLACESEL */
3313 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3314 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3316 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3317 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
3318 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
3319 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
3320 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3321 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3322 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
3323 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
3325 /* Test the actual string */
3327 getText.codepage = CP_ACP;
3328 getText.flags = GT_DEFAULT;
3329 getText.lpDefaultChar = NULL;
3330 getText.lpUsedDefChar = NULL;
3331 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3332 ok(strcmp(buffer, " ") == 0,
3333 "EM_GETTEXTEX returned incorrect string\n");
3335 /* Test number of lines reported after EM_REPLACESEL */
3336 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3337 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3339 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3340 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
3341 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3342 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3343 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3344 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3345 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3346 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3348 /* Test the actual string */
3350 getText.codepage = CP_ACP;
3351 getText.flags = GT_DEFAULT;
3352 getText.lpDefaultChar = NULL;
3353 getText.lpUsedDefChar = NULL;
3354 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3355 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
3356 "EM_GETTEXTEX returned incorrect string\n");
3358 /* Test number of lines reported after EM_REPLACESEL */
3359 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3360 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3362 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3363 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
3364 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
3365 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
3366 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3367 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3368 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3369 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3371 /* Test the actual string */
3373 getText.codepage = CP_ACP;
3374 getText.flags = GT_DEFAULT;
3375 getText.lpDefaultChar = NULL;
3376 getText.lpUsedDefChar = NULL;
3377 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3378 ok(strcmp(buffer, " \r") == 0,
3379 "EM_GETTEXTEX returned incorrect string\n");
3381 /* Test number of lines reported after EM_REPLACESEL */
3382 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3383 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3385 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3386 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
3387 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
3388 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
3389 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3390 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3391 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
3392 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
3394 /* Test the actual string */
3396 getText.codepage = CP_ACP;
3397 getText.flags = GT_DEFAULT;
3398 getText.lpDefaultChar = NULL;
3399 getText.lpUsedDefChar = NULL;
3400 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3401 ok(strcmp(buffer, " \r\r") == 0,
3402 "EM_GETTEXTEX returned incorrect string\n");
3404 /* Test number of lines reported after EM_REPLACESEL */
3405 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3406 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3408 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3409 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
3410 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
3411 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
3412 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3413 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3414 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
3415 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
3417 /* Test the actual string */
3419 getText.codepage = CP_ACP;
3420 getText.flags = GT_DEFAULT;
3421 getText.lpDefaultChar = NULL;
3422 getText.lpUsedDefChar = NULL;
3423 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3424 ok(strcmp(buffer, "\rX\r\r\r") == 0,
3425 "EM_GETTEXTEX returned incorrect string\n");
3427 /* Test number of lines reported after EM_REPLACESEL */
3428 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3429 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
3431 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3432 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
3433 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
3434 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3435 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3436 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3437 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3439 /* Test the actual string */
3441 getText.codepage = CP_ACP;
3442 getText.flags = GT_DEFAULT;
3443 getText.lpDefaultChar = NULL;
3444 getText.lpUsedDefChar = NULL;
3445 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3446 ok(strcmp(buffer, "\r\r") == 0,
3447 "EM_GETTEXTEX returned incorrect string\n");
3449 /* Test number of lines reported after EM_REPLACESEL */
3450 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3451 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3453 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3454 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
3455 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3456 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3457 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3458 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3459 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3460 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3462 /* Test the actual string */
3464 getText.codepage = CP_ACP;
3465 getText.flags = GT_DEFAULT;
3466 getText.lpDefaultChar = NULL;
3467 getText.lpUsedDefChar = NULL;
3468 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3469 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
3470 "EM_GETTEXTEX returned incorrect string\n");
3472 /* Test number of lines reported after EM_REPLACESEL */
3473 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3474 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3477 /* This is needed to avoid interferring with keybd_event calls
3478 * on other tests that simulate keyboard events. */
3479 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
3481 DestroyWindow(hwndRichEdit);
3484 static void test_WM_PASTE(void)
3487 char buffer[1024] = {0};
3488 const char* text1 = "testing paste\r";
3489 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
3490 const char* text1_after = "testing paste\r\n";
3491 const char* text2 = "testing paste\r\rtesting paste";
3492 const char* text2_after = "testing paste\r\n\r\ntesting paste";
3493 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
3494 HWND hwndRichEdit = new_richedit(NULL);
3496 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
3497 messages, probably because it inspects the keyboard state itself.
3498 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
3501 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
3502 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
3503 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
3504 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
3505 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
3507 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
3508 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
3510 SEND_CTRL_C(hwndRichEdit); /* Copy */
3511 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3512 SEND_CTRL_V(hwndRichEdit); /* Paste */
3513 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3514 /* Pasted text should be visible at this step */
3515 result = strcmp(text1_step1, buffer);
3517 "test paste: strcmp = %i\n", result);
3518 SEND_CTRL_Z(hwndRichEdit); /* Undo */
3519 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3520 /* Text should be the same as before (except for \r -> \r\n conversion) */
3521 result = strcmp(text1_after, buffer);
3523 "test paste: strcmp = %i\n", result);
3525 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
3526 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
3527 SEND_CTRL_C(hwndRichEdit); /* Copy */
3528 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3529 SEND_CTRL_V(hwndRichEdit); /* Paste */
3530 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3531 /* Pasted text should be visible at this step */
3532 result = strcmp(text3, buffer);
3534 "test paste: strcmp = %i\n", result);
3535 SEND_CTRL_Z(hwndRichEdit); /* Undo */
3536 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3537 /* Text should be the same as before (except for \r -> \r\n conversion) */
3538 result = strcmp(text2_after, buffer);
3540 "test paste: strcmp = %i\n", result);
3541 SEND_CTRL_Y(hwndRichEdit); /* Redo */
3542 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3543 /* Text should revert to post-paste state */
3544 result = strcmp(buffer,text3);
3546 "test paste: strcmp = %i\n", result);
3548 DestroyWindow(hwndRichEdit);
3551 static void test_EM_FORMATRANGE(void)
3556 HWND hwndRichEdit = new_richedit(NULL);
3558 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
3560 hdc = GetDC(hwndRichEdit);
3561 ok(hdc != NULL, "Could not get HDC\n");
3563 fr.hdc = fr.hdcTarget = hdc;
3564 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
3565 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
3566 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
3570 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3572 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3575 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3577 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
3583 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3585 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
3588 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3590 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3593 DestroyWindow(hwndRichEdit);
3596 static int nCallbackCount = 0;
3598 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
3601 const char text[] = {'t','e','s','t'};
3603 if (sizeof(text) <= cb)
3605 if ((int)dwCookie != nCallbackCount)
3611 memcpy (pbBuff, text, sizeof(text));
3612 *pcb = sizeof(text);
3619 return 1; /* indicates callback failed */
3622 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
3627 const char** str = (const char**)dwCookie;
3628 int size = strlen(*str);
3634 memcpy(pbBuff, *str, *pcb);
3640 struct StringWithLength {
3645 /* This callback is used to handled the null characters in a string. */
3646 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
3651 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
3652 int size = str->length;
3658 memcpy(pbBuff, str->buffer, *pcb);
3659 str->buffer += *pcb;
3660 str->length -= *pcb;
3665 static void test_EM_STREAMIN(void)
3667 HWND hwndRichEdit = new_richedit(NULL);
3670 char buffer[1024] = {0};
3672 const char * streamText0 = "{\\rtf1 TestSomeText}";
3673 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
3674 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
3676 const char * streamText1 =
3677 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
3678 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
3681 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
3682 const char * streamText2 =
3683 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
3684 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
3685 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
3686 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
3687 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
3688 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
3689 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
3691 const char * streamText3 = "RichEdit1";
3693 struct StringWithLength cookieForStream4;
3694 const char * streamText4 =
3695 "This text just needs to be long enough to cause run to be split onto "\
3696 "two separate lines and make sure the null terminating character is "\
3697 "handled properly.\0";
3698 int length4 = strlen(streamText4) + 1;
3699 cookieForStream4.buffer = (char *)streamText4;
3700 cookieForStream4.length = length4;
3702 /* Minimal test without \par at the end */
3703 es.dwCookie = (DWORD_PTR)&streamText0;
3705 es.pfnCallback = test_EM_STREAMIN_esCallback;
3706 SendMessage(hwndRichEdit, EM_STREAMIN,
3707 (WPARAM)(SF_RTF), (LPARAM)&es);
3709 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3711 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
3712 result = strcmp (buffer,"TestSomeText");
3714 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
3715 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
3717 /* Native richedit 2.0 ignores last \par */
3718 es.dwCookie = (DWORD_PTR)&streamText0a;
3720 es.pfnCallback = test_EM_STREAMIN_esCallback;
3721 SendMessage(hwndRichEdit, EM_STREAMIN,
3722 (WPARAM)(SF_RTF), (LPARAM)&es);
3724 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3726 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
3727 result = strcmp (buffer,"TestSomeText");
3729 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
3730 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
3732 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
3733 es.dwCookie = (DWORD_PTR)&streamText0b;
3735 es.pfnCallback = test_EM_STREAMIN_esCallback;
3736 SendMessage(hwndRichEdit, EM_STREAMIN,
3737 (WPARAM)(SF_RTF), (LPARAM)&es);
3739 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3741 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
3742 result = strcmp (buffer,"TestSomeText\r\n");
3744 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
3745 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
3747 es.dwCookie = (DWORD_PTR)&streamText1;
3749 es.pfnCallback = test_EM_STREAMIN_esCallback;
3750 SendMessage(hwndRichEdit, EM_STREAMIN,
3751 (WPARAM)(SF_RTF), (LPARAM)&es);
3753 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3755 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
3756 result = strcmp (buffer,"TestSomeText");
3758 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3759 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
3761 es.dwCookie = (DWORD_PTR)&streamText2;
3763 SendMessage(hwndRichEdit, EM_STREAMIN,
3764 (WPARAM)(SF_RTF), (LPARAM)&es);
3766 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3768 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
3769 ok (strlen(buffer) == 0,
3770 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3771 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
3773 es.dwCookie = (DWORD_PTR)&streamText3;
3775 SendMessage(hwndRichEdit, EM_STREAMIN,
3776 (WPARAM)(SF_RTF), (LPARAM)&es);
3778 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3780 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
3781 ok (strlen(buffer) == 0,
3782 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
3783 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
3785 es.dwCookie = (DWORD_PTR)&cookieForStream4;
3787 es.pfnCallback = test_EM_STREAMIN_esCallback2;
3788 SendMessage(hwndRichEdit, EM_STREAMIN,
3789 (WPARAM)(SF_TEXT), (LPARAM)&es);
3791 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3792 ok (result == length4,
3793 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
3794 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
3796 DestroyWindow(hwndRichEdit);
3799 static void test_EM_StreamIn_Undo(void)
3801 /* The purpose of this test is to determine when a EM_StreamIn should be
3802 * undoable. This is important because WM_PASTE currently uses StreamIn and
3803 * pasting should always be undoable but streaming isn't always.
3806 * StreamIn plain text without SFF_SELECTION.
3807 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
3808 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
3809 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
3810 * Feel free to add tests for other text modes or StreamIn things.
3814 HWND hwndRichEdit = new_richedit(NULL);
3817 char buffer[1024] = {0};
3818 const char randomtext[] = "Some text";
3820 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
3822 /* StreamIn, no SFF_SELECTION */
3823 es.dwCookie = nCallbackCount;
3824 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3825 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3826 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3827 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
3828 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3829 result = strcmp (buffer,"test");
3831 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3833 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3834 ok (result == FALSE,
3835 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
3837 /* StreamIn, SFF_SELECTION, but nothing selected */
3838 es.dwCookie = nCallbackCount;
3839 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3840 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3841 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3842 SendMessage(hwndRichEdit, EM_STREAMIN,
3843 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3844 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3845 result = strcmp (buffer,"testSome text");
3847 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3849 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3851 "EM_STREAMIN with SFF_SELECTION but no selection set "
3852 "should create an undo\n");
3854 /* StreamIn, SFF_SELECTION, with a selection */
3855 es.dwCookie = nCallbackCount;
3856 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3857 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3858 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
3859 SendMessage(hwndRichEdit, EM_STREAMIN,
3860 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3861 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3862 result = strcmp (buffer,"Sometesttext");
3864 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3866 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3868 "EM_STREAMIN with SFF_SELECTION and selection set "
3869 "should create an undo\n");
3871 DestroyWindow(hwndRichEdit);
3874 static BOOL is_em_settextex_supported(HWND hwnd)
3876 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
3877 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
3880 static void test_unicode_conversions(void)
3882 static const WCHAR tW[] = {'t',0};
3883 static const WCHAR teW[] = {'t','e',0};
3884 static const WCHAR textW[] = {'t','e','s','t',0};
3885 static const char textA[] = "test";
3889 int is_win9x, em_settextex_supported, ret;
3891 is_win9x = GetVersion() & 0x80000000;
3893 #define set_textA(hwnd, wm_set_text, txt) \
3895 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
3896 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3897 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3898 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3899 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
3901 #define expect_textA(hwnd, wm_get_text, txt) \
3903 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3904 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
3905 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3906 memset(bufA, 0xAA, sizeof(bufA)); \
3907 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3908 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3909 ret = lstrcmpA(bufA, txt); \
3910 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
3913 #define set_textW(hwnd, wm_set_text, txt) \
3915 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
3916 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3917 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3918 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3919 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
3921 #define expect_textW(hwnd, wm_get_text, txt) \
3923 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
3924 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
3925 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3926 memset(bufW, 0xAA, sizeof(bufW)); \
3929 assert(wm_get_text == EM_GETTEXTEX); \
3930 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3931 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3935 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3936 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
3938 ret = lstrcmpW(bufW, txt); \
3939 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
3941 #define expect_empty(hwnd, wm_get_text) \
3943 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3944 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
3945 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3946 memset(bufA, 0xAA, sizeof(bufA)); \
3947 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3948 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
3949 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
3952 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3953 0, 0, 200, 60, 0, 0, 0, 0);
3954 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3956 ret = IsWindowUnicode(hwnd);
3958 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
3960 ok(ret, "RichEdit20W should be unicode under NT\n");
3962 /* EM_SETTEXTEX is supported starting from version 3.0 */
3963 em_settextex_supported = is_em_settextex_supported(hwnd);
3964 trace("EM_SETTEXTEX is %ssupported on this platform\n",
3965 em_settextex_supported ? "" : "NOT ");
3967 expect_empty(hwnd, WM_GETTEXT);
3968 expect_empty(hwnd, EM_GETTEXTEX);
3970 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
3971 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3972 expect_textA(hwnd, WM_GETTEXT, "t");
3973 expect_textA(hwnd, EM_GETTEXTEX, "t");
3974 expect_textW(hwnd, EM_GETTEXTEX, tW);
3976 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
3977 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3978 expect_textA(hwnd, WM_GETTEXT, "te");
3979 expect_textA(hwnd, EM_GETTEXTEX, "te");
3980 expect_textW(hwnd, EM_GETTEXTEX, teW);
3982 set_textA(hwnd, WM_SETTEXT, NULL);
3983 expect_empty(hwnd, WM_GETTEXT);
3984 expect_empty(hwnd, EM_GETTEXTEX);
3987 set_textA(hwnd, WM_SETTEXT, textW);
3989 set_textA(hwnd, WM_SETTEXT, textA);
3990 expect_textA(hwnd, WM_GETTEXT, textA);
3991 expect_textA(hwnd, EM_GETTEXTEX, textA);
3992 expect_textW(hwnd, EM_GETTEXTEX, textW);
3994 if (em_settextex_supported)
3996 set_textA(hwnd, EM_SETTEXTEX, textA);
3997 expect_textA(hwnd, WM_GETTEXT, textA);
3998 expect_textA(hwnd, EM_GETTEXTEX, textA);
3999 expect_textW(hwnd, EM_GETTEXTEX, textW);
4004 set_textW(hwnd, WM_SETTEXT, textW);
4005 expect_textW(hwnd, WM_GETTEXT, textW);
4006 expect_textA(hwnd, WM_GETTEXT, textA);
4007 expect_textW(hwnd, EM_GETTEXTEX, textW);
4008 expect_textA(hwnd, EM_GETTEXTEX, textA);
4010 if (em_settextex_supported)
4012 set_textW(hwnd, EM_SETTEXTEX, textW);
4013 expect_textW(hwnd, WM_GETTEXT, textW);
4014 expect_textA(hwnd, WM_GETTEXT, textA);
4015 expect_textW(hwnd, EM_GETTEXTEX, textW);
4016 expect_textA(hwnd, EM_GETTEXTEX, textA);
4019 DestroyWindow(hwnd);
4021 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
4022 0, 0, 200, 60, 0, 0, 0, 0);
4023 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4025 ret = IsWindowUnicode(hwnd);
4026 ok(!ret, "RichEdit20A should NOT be unicode\n");
4028 set_textA(hwnd, WM_SETTEXT, textA);
4029 expect_textA(hwnd, WM_GETTEXT, textA);
4030 expect_textA(hwnd, EM_GETTEXTEX, textA);
4031 expect_textW(hwnd, EM_GETTEXTEX, textW);
4033 if (em_settextex_supported)
4035 set_textA(hwnd, EM_SETTEXTEX, textA);
4036 expect_textA(hwnd, WM_GETTEXT, textA);
4037 expect_textA(hwnd, EM_GETTEXTEX, textA);
4038 expect_textW(hwnd, EM_GETTEXTEX, textW);
4043 set_textW(hwnd, WM_SETTEXT, textW);
4044 expect_textW(hwnd, WM_GETTEXT, textW);
4045 expect_textA(hwnd, WM_GETTEXT, textA);
4046 expect_textW(hwnd, EM_GETTEXTEX, textW);
4047 expect_textA(hwnd, EM_GETTEXTEX, textA);
4049 if (em_settextex_supported)
4051 set_textW(hwnd, EM_SETTEXTEX, textW);
4052 expect_textW(hwnd, WM_GETTEXT, textW);
4053 expect_textA(hwnd, WM_GETTEXT, textA);
4054 expect_textW(hwnd, EM_GETTEXTEX, textW);
4055 expect_textA(hwnd, EM_GETTEXTEX, textA);
4058 DestroyWindow(hwnd);
4061 static void test_WM_CHAR(void)
4065 const char * char_list = "abc\rabc\r";
4066 const char * expected_content_single = "abcabc";
4067 const char * expected_content_multi = "abc\r\nabc\r\n";
4068 char buffer[64] = {0};
4071 /* single-line control must IGNORE carriage returns */
4072 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4073 0, 0, 200, 60, 0, 0, 0, 0);
4074 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4077 while (*p != '\0') {
4078 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4079 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4080 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4081 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4085 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4086 ret = strcmp(buffer, expected_content_single);
4087 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4089 DestroyWindow(hwnd);
4091 /* multi-line control inserts CR normally */
4092 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4093 0, 0, 200, 60, 0, 0, 0, 0);
4094 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4097 while (*p != '\0') {
4098 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4099 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4100 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4101 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4105 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4106 ret = strcmp(buffer, expected_content_multi);
4107 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4109 DestroyWindow(hwnd);
4112 static void test_EM_GETTEXTLENGTHEX(void)
4115 GETTEXTLENGTHEX gtl;
4117 const char * base_string = "base string";
4118 const char * test_string = "a\nb\n\n\r\n";
4119 const char * test_string_after = "a";
4120 const char * test_string_2 = "a\rtest\rstring";
4121 char buffer[64] = {0};
4124 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4125 0, 0, 200, 60, 0, 0, 0, 0);
4126 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4128 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4129 gtl.codepage = CP_ACP;
4130 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4131 ok(ret == 0, "ret %d\n",ret);
4133 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4134 gtl.codepage = CP_ACP;
4135 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4136 ok(ret == 0, "ret %d\n",ret);
4138 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4140 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4141 gtl.codepage = CP_ACP;
4142 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4143 ok(ret == strlen(base_string), "ret %d\n",ret);
4145 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4146 gtl.codepage = CP_ACP;
4147 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4148 ok(ret == strlen(base_string), "ret %d\n",ret);
4150 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4152 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4153 gtl.codepage = CP_ACP;
4154 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4155 ok(ret == 1, "ret %d\n",ret);
4157 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4158 gtl.codepage = CP_ACP;
4159 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4160 ok(ret == 1, "ret %d\n",ret);
4162 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4163 ret = strcmp(buffer, test_string_after);
4164 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4166 DestroyWindow(hwnd);
4169 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
4170 0, 0, 200, 60, 0, 0, 0, 0);
4171 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4173 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4174 gtl.codepage = CP_ACP;
4175 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4176 ok(ret == 0, "ret %d\n",ret);
4178 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4179 gtl.codepage = CP_ACP;
4180 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4181 ok(ret == 0, "ret %d\n",ret);
4183 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4185 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4186 gtl.codepage = CP_ACP;
4187 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4188 ok(ret == strlen(base_string), "ret %d\n",ret);
4190 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4191 gtl.codepage = CP_ACP;
4192 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4193 ok(ret == strlen(base_string), "ret %d\n",ret);
4195 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
4197 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4198 gtl.codepage = CP_ACP;
4199 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4200 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
4202 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4203 gtl.codepage = CP_ACP;
4204 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4205 ok(ret == strlen(test_string_2), "ret %d\n",ret);
4207 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4209 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4210 gtl.codepage = CP_ACP;
4211 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4212 ok(ret == 10, "ret %d\n",ret);
4214 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4215 gtl.codepage = CP_ACP;
4216 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4217 ok(ret == 6, "ret %d\n",ret);
4219 DestroyWindow(hwnd);
4223 /* globals that parent and child access when checking event masks & notifications */
4224 static HWND eventMaskEditHwnd = 0;
4225 static int queriedEventMask;
4226 static int watchForEventMask = 0;
4228 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
4229 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4231 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
4233 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4235 return DefWindowProcA(hwnd, message, wParam, lParam);
4238 /* test event masks in combination with WM_COMMAND */
4239 static void test_eventMask(void)
4244 const char text[] = "foo bar\n";
4247 /* register class to capture WM_COMMAND */
4249 cls.lpfnWndProc = ParentMsgCheckProcA;
4252 cls.hInstance = GetModuleHandleA(0);
4254 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4255 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4256 cls.lpszMenuName = NULL;
4257 cls.lpszClassName = "EventMaskParentClass";
4258 if(!RegisterClassA(&cls)) assert(0);
4260 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4261 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4262 ok (parent != 0, "Failed to create parent window\n");
4264 eventMaskEditHwnd = new_richedit(parent);
4265 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
4267 eventMask = ENM_CHANGE | ENM_UPDATE;
4268 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
4269 ok(ret == ENM_NONE, "wrong event mask\n");
4270 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4271 ok(ret == eventMask, "failed to set event mask\n");
4273 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
4274 queriedEventMask = 0; /* initialize to something other than we expect */
4275 watchForEventMask = EN_CHANGE;
4276 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
4277 ok(ret == TRUE, "failed to set text\n");
4278 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
4279 notification in response to WM_SETTEXT */
4280 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
4281 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
4285 static int received_WM_NOTIFY = 0;
4286 static int modify_at_WM_NOTIFY = 0;
4287 static HWND hwndRichedit_WM_NOTIFY;
4289 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4291 if(message == WM_NOTIFY)
4293 received_WM_NOTIFY = 1;
4294 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
4296 return DefWindowProcA(hwnd, message, wParam, lParam);
4299 static void test_WM_NOTIFY(void)
4305 /* register class to capture WM_NOTIFY */
4307 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
4310 cls.hInstance = GetModuleHandleA(0);
4312 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4313 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4314 cls.lpszMenuName = NULL;
4315 cls.lpszClassName = "WM_NOTIFY_ParentClass";
4316 if(!RegisterClassA(&cls)) assert(0);
4318 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4319 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4320 ok (parent != 0, "Failed to create parent window\n");
4322 hwndRichedit_WM_NOTIFY = new_richedit(parent);
4323 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
4325 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
4327 /* Notifications for selection change should only be sent when selection
4328 actually changes. EM_SETCHARFORMAT is one message that calls
4329 ME_CommitUndo, which should check whether message should be sent */
4330 received_WM_NOTIFY = 0;
4331 cf2.cbSize = sizeof(CHARFORMAT2);
4332 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4334 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4335 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4336 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
4337 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4339 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
4341 received_WM_NOTIFY = 0;
4342 modify_at_WM_NOTIFY = 0;
4343 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4344 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4345 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4347 received_WM_NOTIFY = 0;
4348 modify_at_WM_NOTIFY = 0;
4349 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
4350 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4352 received_WM_NOTIFY = 0;
4353 modify_at_WM_NOTIFY = 0;
4354 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4355 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4356 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4358 DestroyWindow(hwndRichedit_WM_NOTIFY);
4359 DestroyWindow(parent);
4362 static void test_undo_coalescing(void)
4366 char buffer[64] = {0};
4368 /* multi-line control inserts CR normally */
4369 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4370 0, 0, 200, 60, 0, 0, 0, 0);
4371 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4373 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4374 ok (result == FALSE, "Can undo after window creation.\n");
4375 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4376 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
4377 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4378 ok (result == FALSE, "Can redo after window creation.\n");
4379 result = SendMessage(hwnd, EM_REDO, 0, 0);
4380 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
4382 /* Test the effect of arrows keys during typing on undo transactions*/
4383 simulate_typing_characters(hwnd, "one two three");
4384 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
4385 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
4386 simulate_typing_characters(hwnd, " four five six");
4388 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4389 ok (result == FALSE, "Can redo before anything is undone.\n");
4390 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4391 ok (result == TRUE, "Cannot undo typed characters.\n");
4392 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4393 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
4394 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4395 ok (result == TRUE, "Cannot redo after undo.\n");
4396 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4397 result = strcmp(buffer, "one two three");
4398 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4400 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4401 ok (result == TRUE, "Cannot undo typed characters.\n");
4402 result = SendMessage(hwnd, WM_UNDO, 0, 0);
4403 ok (result == TRUE, "Failed to undo typed characters.\n");
4404 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4405 result = strcmp(buffer, "");
4406 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4408 /* Test the effect of focus changes during typing on undo transactions*/
4409 simulate_typing_characters(hwnd, "one two three");
4410 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4411 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
4412 SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
4413 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
4414 simulate_typing_characters(hwnd, " four five six");
4415 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4416 ok (result == TRUE, "Failed to undo typed characters.\n");
4417 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4418 result = strcmp(buffer, "one two three");
4419 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4421 /* Test the effect of the back key during typing on undo transactions */
4422 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4423 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
4424 ok (result == TRUE, "Failed to clear the text.\n");
4425 simulate_typing_characters(hwnd, "one two threa");
4426 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4427 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
4428 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
4429 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
4430 simulate_typing_characters(hwnd, "e four five six");
4431 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4432 ok (result == TRUE, "Failed to undo typed characters.\n");
4433 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4434 result = strcmp(buffer, "");
4435 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4437 /* Test the effect of the delete key during typing on undo transactions */
4438 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4439 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
4440 ok(result == TRUE, "Failed to set the text.\n");
4441 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
4442 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
4443 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
4444 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
4445 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
4446 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4447 ok (result == TRUE, "Failed to undo typed characters.\n");
4448 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4449 result = strcmp(buffer, "acd");
4450 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
4451 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4452 ok (result == TRUE, "Failed to undo typed characters.\n");
4453 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4454 result = strcmp(buffer, "abcd");
4455 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
4457 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
4458 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4459 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
4460 ok (result == TRUE, "Failed to clear the text.\n");
4461 simulate_typing_characters(hwnd, "one two three");
4462 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
4463 ok (result == 0, "expected %d but got %d\n", 0, result);
4464 simulate_typing_characters(hwnd, " four five six");
4465 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4466 ok (result == TRUE, "Failed to undo typed characters.\n");
4467 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4468 result = strcmp(buffer, "one two three");
4469 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4470 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4471 ok (result == TRUE, "Failed to undo typed characters.\n");
4472 ok (result == TRUE, "Failed to undo typed characters.\n");
4473 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4474 result = strcmp(buffer, "");
4475 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4477 DestroyWindow(hwnd);
4480 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
4481 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
4483 static void test_word_movement(void)
4487 int sel_start, sel_end;
4489 /* multi-line control inserts CR normally */
4490 hwnd = new_richedit(NULL);
4492 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
4493 ok (result == TRUE, "Failed to clear the text.\n");
4494 SendMessage(hwnd, EM_SETSEL, 0, 0);
4495 /* |one two three */
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 == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
4503 SEND_CTRL_RIGHT(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 == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
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 == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
4515 SEND_CTRL_LEFT(hwnd);
4516 /* |one two three */
4517 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4518 ok(sel_start == sel_end, "Selection should be empty\n");
4519 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
4521 SendMessage(hwnd, EM_SETSEL, 8, 8);
4522 /* one two | three */
4523 SEND_CTRL_RIGHT(hwnd);
4524 /* one two |three */
4525 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4526 ok(sel_start == sel_end, "Selection should be empty\n");
4527 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
4529 SendMessage(hwnd, EM_SETSEL, 11, 11);
4530 /* one two th|ree */
4531 SEND_CTRL_LEFT(hwnd);
4532 /* one two |three */
4533 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4534 ok(sel_start == sel_end, "Selection should be empty\n");
4535 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
4537 DestroyWindow(hwnd);
4540 static void test_EM_CHARFROMPOS(void)
4548 /* multi-line control inserts CR normally */
4549 hwnd = new_richedit(NULL);
4550 result = SendMessageA(hwnd, WM_SETTEXT, 0,
4551 (LPARAM)"one two three four five six seven");
4553 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
4554 ok(result == 0, "expected character index of 0 but got %d\n", result);
4556 DestroyWindow(hwnd);
4559 START_TEST( editor )
4564 /* Must explicitly LoadLibrary(). The test has no references to functions in
4565 * RICHED20.DLL, so the linker doesn't actually link to it. */
4566 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
4567 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
4571 test_EM_POSFROMCHAR();
4572 test_EM_SCROLLCARET();
4575 test_EM_LINELENGTH();
4576 test_EM_SETCHARFORMAT();
4577 test_EM_SETTEXTMODE();
4578 test_TM_PLAINTEXT();
4579 test_EM_SETOPTIONS();
4581 test_EM_GETTEXTRANGE();
4582 test_EM_GETSELTEXT();
4583 test_EM_SETUNDOLIMIT();
4585 test_EM_SETTEXTEX();
4586 test_EM_LIMITTEXT();
4587 test_EM_EXLIMITTEXT();
4588 test_EM_GETLIMITTEXT();
4590 test_EM_GETMODIFY();
4594 test_EM_STREAMOUT();
4595 test_EM_StreamIn_Undo();
4596 test_EM_FORMATRANGE();
4597 test_unicode_conversions();
4598 test_EM_GETTEXTLENGTHEX();
4599 test_EM_REPLACESEL(1);
4600 test_EM_REPLACESEL(0);
4602 test_EM_AUTOURLDETECT();
4604 test_undo_coalescing();
4605 test_word_movement();
4606 test_EM_CHARFROMPOS();
4608 /* Set the environment variable WINETEST_RICHED20 to keep windows
4609 * responsive and open for 30 seconds. This is useful for debugging.
4611 * The message pump uses PeekMessage() to empty the queue and then sleeps for
4612 * 50ms before retrying the queue. */
4613 end = time(NULL) + 30;
4614 if (getenv( "WINETEST_RICHED20" )) {
4615 while (time(NULL) < end) {
4616 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
4617 TranslateMessage(&msg);
4618 DispatchMessage(&msg);
4625 OleFlushClipboard();
4626 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());