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 unsigned int recursionLevel = 0;
2233 unsigned int WM_SIZE_recursionLevel = 0;
2234 BOOL bailedOutOfRecursion = FALSE;
2235 LRESULT WINAPI (*richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2237 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2241 if (bailedOutOfRecursion) return 0;
2242 if (recursionLevel >= 32) {
2243 bailedOutOfRecursion = TRUE;
2250 WM_SIZE_recursionLevel++;
2251 r = richeditProc(hwnd, message, wParam, lParam);
2252 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2253 ShowScrollBar(hwnd, SB_VERT, TRUE);
2254 WM_SIZE_recursionLevel--;
2257 r = richeditProc(hwnd, message, wParam, lParam);
2264 static void test_scrollbar_visibility(void)
2267 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2272 /* These tests show that richedit should temporarily refrain from automatically
2273 hiding or showing its scrollbars (vertical at least) when an explicit request
2274 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2275 Some applications depend on forced showing (when otherwise richedit would
2276 hide the vertical scrollbar) and are thrown on an endless recursive loop
2277 if richedit auto-hides the scrollbar again. Apparently they never heard of
2278 the ES_DISABLENOSCROLL style... */
2280 hwndRichEdit = new_richedit(NULL);
2282 /* Test default scrollbar visibility behavior */
2283 memset(&si, 0, sizeof(si));
2284 si.cbSize = sizeof(si);
2285 si.fMask = SIF_PAGE | SIF_RANGE;
2286 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2287 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2288 "Vertical scrollbar is visible, should be invisible.\n");
2289 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2290 "reported page/range is %d (%d..%d) expected all 0\n",
2291 si.nPage, si.nMin, si.nMax);
2293 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2294 memset(&si, 0, sizeof(si));
2295 si.cbSize = sizeof(si);
2296 si.fMask = SIF_PAGE | SIF_RANGE;
2297 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2298 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2299 "Vertical scrollbar is visible, should be invisible.\n");
2300 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2301 "reported page/range is %d (%d..%d) expected all 0\n",
2302 si.nPage, si.nMin, si.nMax);
2304 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2305 memset(&si, 0, sizeof(si));
2306 si.cbSize = sizeof(si);
2307 si.fMask = SIF_PAGE | SIF_RANGE;
2308 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2309 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2310 "Vertical scrollbar is invisible, should be visible.\n");
2311 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2312 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2313 si.nPage, si.nMin, si.nMax);
2315 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2316 even though it hides the scrollbar */
2317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2318 memset(&si, 0, sizeof(si));
2319 si.cbSize = sizeof(si);
2320 si.fMask = SIF_PAGE | SIF_RANGE;
2321 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2322 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2323 "Vertical scrollbar is visible, should be invisible.\n");
2324 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2325 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2326 si.nPage, si.nMin, si.nMax);
2328 /* Setting non-scrolling text again does *not* reset scrollbar range */
2329 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2330 memset(&si, 0, sizeof(si));
2331 si.cbSize = sizeof(si);
2332 si.fMask = SIF_PAGE | SIF_RANGE;
2333 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2334 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2335 "Vertical scrollbar is visible, should be invisible.\n");
2336 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2337 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2338 si.nPage, si.nMin, si.nMax);
2340 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2341 memset(&si, 0, sizeof(si));
2342 si.cbSize = sizeof(si);
2343 si.fMask = SIF_PAGE | SIF_RANGE;
2344 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2345 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2346 "Vertical scrollbar is visible, should be invisible.\n");
2347 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2348 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2349 si.nPage, si.nMin, si.nMax);
2351 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2352 memset(&si, 0, sizeof(si));
2353 si.cbSize = sizeof(si);
2354 si.fMask = SIF_PAGE | SIF_RANGE;
2355 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2356 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2357 "Vertical scrollbar is visible, should be invisible.\n");
2358 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2359 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2360 si.nPage, si.nMin, si.nMax);
2362 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2363 memset(&si, 0, sizeof(si));
2364 si.cbSize = sizeof(si);
2365 si.fMask = SIF_PAGE | SIF_RANGE;
2366 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2367 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2368 "Vertical scrollbar is visible, should be invisible.\n");
2369 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2370 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2371 si.nPage, si.nMin, si.nMax);
2373 DestroyWindow(hwndRichEdit);
2375 /* Test again, with ES_DISABLENOSCROLL style */
2376 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2378 /* Test default scrollbar visibility behavior */
2379 memset(&si, 0, sizeof(si));
2380 si.cbSize = sizeof(si);
2381 si.fMask = SIF_PAGE | SIF_RANGE;
2382 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2383 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2384 "Vertical scrollbar is invisible, should be visible.\n");
2385 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2386 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2387 si.nPage, si.nMin, si.nMax);
2389 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2390 memset(&si, 0, sizeof(si));
2391 si.cbSize = sizeof(si);
2392 si.fMask = SIF_PAGE | SIF_RANGE;
2393 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2394 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2395 "Vertical scrollbar is invisible, should be visible.\n");
2396 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2397 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2398 si.nPage, si.nMin, si.nMax);
2400 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2401 memset(&si, 0, sizeof(si));
2402 si.cbSize = sizeof(si);
2403 si.fMask = SIF_PAGE | SIF_RANGE;
2404 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2405 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2406 "Vertical scrollbar is invisible, should be visible.\n");
2407 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2408 "reported page/range is %d (%d..%d)\n",
2409 si.nPage, si.nMin, si.nMax);
2411 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2412 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2413 memset(&si, 0, sizeof(si));
2414 si.cbSize = sizeof(si);
2415 si.fMask = SIF_PAGE | SIF_RANGE;
2416 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2417 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2418 "Vertical scrollbar is invisible, should be visible.\n");
2419 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2420 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2421 si.nPage, si.nMin, si.nMax);
2423 /* Setting non-scrolling text again does *not* reset scrollbar range */
2424 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2425 memset(&si, 0, sizeof(si));
2426 si.cbSize = sizeof(si);
2427 si.fMask = SIF_PAGE | SIF_RANGE;
2428 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2429 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2430 "Vertical scrollbar is invisible, should be visible.\n");
2431 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2432 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2433 si.nPage, si.nMin, si.nMax);
2435 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2436 memset(&si, 0, sizeof(si));
2437 si.cbSize = sizeof(si);
2438 si.fMask = SIF_PAGE | SIF_RANGE;
2439 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2440 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2441 "Vertical scrollbar is invisible, should be visible.\n");
2442 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2443 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2444 si.nPage, si.nMin, si.nMax);
2446 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2447 memset(&si, 0, sizeof(si));
2448 si.cbSize = sizeof(si);
2449 si.fMask = SIF_PAGE | SIF_RANGE;
2450 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2451 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2452 "Vertical scrollbar is invisible, should be visible.\n");
2453 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2454 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2455 si.nPage, si.nMin, si.nMax);
2457 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2458 memset(&si, 0, sizeof(si));
2459 si.cbSize = sizeof(si);
2460 si.fMask = SIF_PAGE | SIF_RANGE;
2461 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2462 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2463 "Vertical scrollbar is invisible, should be visible.\n");
2464 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2465 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2466 si.nPage, si.nMin, si.nMax);
2468 DestroyWindow(hwndRichEdit);
2470 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2471 hwndRichEdit = new_richedit(NULL);
2473 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2474 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2475 memset(&si, 0, sizeof(si));
2476 si.cbSize = sizeof(si);
2477 si.fMask = SIF_PAGE | SIF_RANGE;
2478 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2479 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2480 "Vertical scrollbar is invisible, should be visible.\n");
2482 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2483 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2484 si.nPage, si.nMin, si.nMax);
2487 /* Ditto, see above */
2488 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2489 memset(&si, 0, sizeof(si));
2490 si.cbSize = sizeof(si);
2491 si.fMask = SIF_PAGE | SIF_RANGE;
2492 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2493 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2494 "Vertical scrollbar is invisible, should be visible.\n");
2496 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2497 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2498 si.nPage, si.nMin, si.nMax);
2501 /* Ditto, see above */
2502 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2503 memset(&si, 0, sizeof(si));
2504 si.cbSize = sizeof(si);
2505 si.fMask = SIF_PAGE | SIF_RANGE;
2506 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2507 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2508 "Vertical scrollbar is invisible, should be visible.\n");
2510 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2511 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2512 si.nPage, si.nMin, si.nMax);
2515 /* Ditto, see above */
2516 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2517 memset(&si, 0, sizeof(si));
2518 si.cbSize = sizeof(si);
2519 si.fMask = SIF_PAGE | SIF_RANGE;
2520 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2521 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2522 "Vertical scrollbar is invisible, should be visible.\n");
2524 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2525 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2526 si.nPage, si.nMin, si.nMax);
2529 /* Ditto, see above */
2530 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2531 memset(&si, 0, sizeof(si));
2532 si.cbSize = sizeof(si);
2533 si.fMask = SIF_PAGE | SIF_RANGE;
2534 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2535 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2536 "Vertical scrollbar is invisible, should be visible.\n");
2538 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2539 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2540 si.nPage, si.nMin, si.nMax);
2543 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2544 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2545 memset(&si, 0, sizeof(si));
2546 si.cbSize = sizeof(si);
2547 si.fMask = SIF_PAGE | SIF_RANGE;
2548 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2549 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2550 "Vertical scrollbar is visible, should be invisible.\n");
2551 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2552 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2553 si.nPage, si.nMin, si.nMax);
2555 DestroyWindow(hwndRichEdit);
2557 hwndRichEdit = new_richedit(NULL);
2559 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2560 memset(&si, 0, sizeof(si));
2561 si.cbSize = sizeof(si);
2562 si.fMask = SIF_PAGE | SIF_RANGE;
2563 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2564 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2565 "Vertical scrollbar is visible, should be invisible.\n");
2566 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2567 "reported page/range is %d (%d..%d) expected all 0\n",
2568 si.nPage, si.nMin, si.nMax);
2570 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2571 memset(&si, 0, sizeof(si));
2572 si.cbSize = sizeof(si);
2573 si.fMask = SIF_PAGE | SIF_RANGE;
2574 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2575 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2576 "Vertical scrollbar is visible, should be invisible.\n");
2577 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2578 "reported page/range is %d (%d..%d) expected all 0\n",
2579 si.nPage, si.nMin, si.nMax);
2581 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2582 memset(&si, 0, sizeof(si));
2583 si.cbSize = sizeof(si);
2584 si.fMask = SIF_PAGE | SIF_RANGE;
2585 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2586 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2587 "Vertical scrollbar is visible, should be invisible.\n");
2588 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2589 "reported page/range is %d (%d..%d) expected all 0\n",
2590 si.nPage, si.nMin, si.nMax);
2592 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2593 memset(&si, 0, sizeof(si));
2594 si.cbSize = sizeof(si);
2595 si.fMask = SIF_PAGE | SIF_RANGE;
2596 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2597 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2598 "Vertical scrollbar is visible, should be invisible.\n");
2599 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2600 "reported page/range is %d (%d..%d) expected all 0\n",
2601 si.nPage, si.nMin, si.nMax);
2603 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2604 memset(&si, 0, sizeof(si));
2605 si.cbSize = sizeof(si);
2606 si.fMask = SIF_PAGE | SIF_RANGE;
2607 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2608 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2609 "Vertical scrollbar is invisible, should be visible.\n");
2610 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2611 "reported page/range is %d (%d..%d)\n",
2612 si.nPage, si.nMin, si.nMax);
2614 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2615 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2616 memset(&si, 0, sizeof(si));
2617 si.cbSize = sizeof(si);
2618 si.fMask = SIF_PAGE | SIF_RANGE;
2619 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2620 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2621 "Vertical scrollbar is visible, should be invisible.\n");
2622 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2623 "reported page/range is %d (%d..%d)\n",
2624 si.nPage, si.nMin, si.nMax);
2626 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2627 memset(&si, 0, sizeof(si));
2628 si.cbSize = sizeof(si);
2629 si.fMask = SIF_PAGE | SIF_RANGE;
2630 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2631 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2632 "Vertical scrollbar is visible, should be invisible.\n");
2633 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2634 "reported page/range is %d (%d..%d)\n",
2635 si.nPage, si.nMin, si.nMax);
2637 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2638 EM_SCROLL will make visible any forcefully invisible scrollbar */
2639 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2640 memset(&si, 0, sizeof(si));
2641 si.cbSize = sizeof(si);
2642 si.fMask = SIF_PAGE | SIF_RANGE;
2643 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2644 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2645 "Vertical scrollbar is invisible, should be visible.\n");
2646 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2647 "reported page/range is %d (%d..%d)\n",
2648 si.nPage, si.nMin, si.nMax);
2650 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2651 memset(&si, 0, sizeof(si));
2652 si.cbSize = sizeof(si);
2653 si.fMask = SIF_PAGE | SIF_RANGE;
2654 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2655 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2656 "Vertical scrollbar is visible, should be invisible.\n");
2657 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2658 "reported page/range is %d (%d..%d)\n",
2659 si.nPage, si.nMin, si.nMax);
2661 /* Again, EM_SCROLL, with SB_LINEUP */
2662 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2663 memset(&si, 0, sizeof(si));
2664 si.cbSize = sizeof(si);
2665 si.fMask = SIF_PAGE | SIF_RANGE;
2666 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2667 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2668 "Vertical scrollbar is invisible, should be visible.\n");
2669 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2670 "reported page/range is %d (%d..%d)\n",
2671 si.nPage, si.nMin, si.nMax);
2673 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2674 memset(&si, 0, sizeof(si));
2675 si.cbSize = sizeof(si);
2676 si.fMask = SIF_PAGE | SIF_RANGE;
2677 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2678 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2679 "Vertical scrollbar is visible, should be invisible.\n");
2680 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2681 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2682 si.nPage, si.nMin, si.nMax);
2684 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2685 memset(&si, 0, sizeof(si));
2686 si.cbSize = sizeof(si);
2687 si.fMask = SIF_PAGE | SIF_RANGE;
2688 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2689 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2690 "Vertical scrollbar is invisible, should be visible.\n");
2691 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2692 "reported page/range is %d (%d..%d)\n",
2693 si.nPage, si.nMin, si.nMax);
2695 DestroyWindow(hwndRichEdit);
2698 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2699 hwndRichEdit = new_richedit(NULL);
2701 #define ENABLE_WS_VSCROLL(hwnd) \
2702 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2703 #define DISABLE_WS_VSCROLL(hwnd) \
2704 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2706 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2707 ENABLE_WS_VSCROLL(hwndRichEdit);
2708 memset(&si, 0, sizeof(si));
2709 si.cbSize = sizeof(si);
2710 si.fMask = SIF_PAGE | SIF_RANGE;
2711 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2712 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2713 "Vertical scrollbar is invisible, should be visible.\n");
2714 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2715 "reported page/range is %d (%d..%d) expected all 0\n",
2716 si.nPage, si.nMin, si.nMax);
2718 /* Ditto, see above */
2719 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2720 memset(&si, 0, sizeof(si));
2721 si.cbSize = sizeof(si);
2722 si.fMask = SIF_PAGE | SIF_RANGE;
2723 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2724 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2725 "Vertical scrollbar is invisible, should be visible.\n");
2726 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2727 "reported page/range is %d (%d..%d) expected all 0\n",
2728 si.nPage, si.nMin, si.nMax);
2730 /* Ditto, see above */
2731 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2732 memset(&si, 0, sizeof(si));
2733 si.cbSize = sizeof(si);
2734 si.fMask = SIF_PAGE | SIF_RANGE;
2735 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2736 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2737 "Vertical scrollbar is invisible, should be visible.\n");
2738 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2739 "reported page/range is %d (%d..%d) expected all 0\n",
2740 si.nPage, si.nMin, si.nMax);
2742 /* Ditto, see above */
2743 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2744 memset(&si, 0, sizeof(si));
2745 si.cbSize = sizeof(si);
2746 si.fMask = SIF_PAGE | SIF_RANGE;
2747 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2748 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2749 "Vertical scrollbar is invisible, should be visible.\n");
2750 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2751 "reported page/range is %d (%d..%d) expected all 0\n",
2752 si.nPage, si.nMin, si.nMax);
2754 /* Ditto, see above */
2755 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2756 memset(&si, 0, sizeof(si));
2757 si.cbSize = sizeof(si);
2758 si.fMask = SIF_PAGE | SIF_RANGE;
2759 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2760 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2761 "Vertical scrollbar is invisible, should be visible.\n");
2762 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2763 "reported page/range is %d (%d..%d) expected all 0\n",
2764 si.nPage, si.nMin, si.nMax);
2766 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2767 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2768 memset(&si, 0, sizeof(si));
2769 si.cbSize = sizeof(si);
2770 si.fMask = SIF_PAGE | SIF_RANGE;
2771 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2772 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2773 "Vertical scrollbar is visible, should be invisible.\n");
2774 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2775 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2776 si.nPage, si.nMin, si.nMax);
2778 DestroyWindow(hwndRichEdit);
2780 hwndRichEdit = new_richedit(NULL);
2782 DISABLE_WS_VSCROLL(hwndRichEdit);
2783 memset(&si, 0, sizeof(si));
2784 si.cbSize = sizeof(si);
2785 si.fMask = SIF_PAGE | SIF_RANGE;
2786 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2787 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2788 "Vertical scrollbar is visible, should be invisible.\n");
2789 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2790 "reported page/range is %d (%d..%d) expected all 0\n",
2791 si.nPage, si.nMin, si.nMax);
2793 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2794 memset(&si, 0, sizeof(si));
2795 si.cbSize = sizeof(si);
2796 si.fMask = SIF_PAGE | SIF_RANGE;
2797 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2798 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2799 "Vertical scrollbar is visible, should be invisible.\n");
2800 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2801 "reported page/range is %d (%d..%d) expected all 0\n",
2802 si.nPage, si.nMin, si.nMax);
2804 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2805 memset(&si, 0, sizeof(si));
2806 si.cbSize = sizeof(si);
2807 si.fMask = SIF_PAGE | SIF_RANGE;
2808 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2809 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2810 "Vertical scrollbar is visible, should be invisible.\n");
2811 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2812 "reported page/range is %d (%d..%d) expected all 0\n",
2813 si.nPage, si.nMin, si.nMax);
2815 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2816 memset(&si, 0, sizeof(si));
2817 si.cbSize = sizeof(si);
2818 si.fMask = SIF_PAGE | SIF_RANGE;
2819 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2820 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2821 "Vertical scrollbar is visible, should be invisible.\n");
2822 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2823 "reported page/range is %d (%d..%d) expected all 0\n",
2824 si.nPage, si.nMin, si.nMax);
2826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2827 memset(&si, 0, sizeof(si));
2828 si.cbSize = sizeof(si);
2829 si.fMask = SIF_PAGE | SIF_RANGE;
2830 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2832 "Vertical scrollbar is invisible, should be visible.\n");
2833 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2834 "reported page/range is %d (%d..%d)\n",
2835 si.nPage, si.nMin, si.nMax);
2837 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2838 DISABLE_WS_VSCROLL(hwndRichEdit);
2839 memset(&si, 0, sizeof(si));
2840 si.cbSize = sizeof(si);
2841 si.fMask = SIF_PAGE | SIF_RANGE;
2842 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2843 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2844 "Vertical scrollbar is visible, should be invisible.\n");
2845 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2846 "reported page/range is %d (%d..%d)\n",
2847 si.nPage, si.nMin, si.nMax);
2849 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2850 memset(&si, 0, sizeof(si));
2851 si.cbSize = sizeof(si);
2852 si.fMask = SIF_PAGE | SIF_RANGE;
2853 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2854 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2855 "Vertical scrollbar is visible, should be invisible.\n");
2856 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2857 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2858 si.nPage, si.nMin, si.nMax);
2860 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2861 memset(&si, 0, sizeof(si));
2862 si.cbSize = sizeof(si);
2863 si.fMask = SIF_PAGE | SIF_RANGE;
2864 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2865 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2866 "Vertical scrollbar is invisible, should be visible.\n");
2867 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2868 "reported page/range is %d (%d..%d)\n",
2869 si.nPage, si.nMin, si.nMax);
2871 DISABLE_WS_VSCROLL(hwndRichEdit);
2872 memset(&si, 0, sizeof(si));
2873 si.cbSize = sizeof(si);
2874 si.fMask = SIF_PAGE | SIF_RANGE;
2875 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2876 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2877 "Vertical scrollbar is visible, should be invisible.\n");
2878 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2879 "reported page/range is %d (%d..%d)\n",
2880 si.nPage, si.nMin, si.nMax);
2882 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2883 EM_SCROLL will make visible any forcefully invisible scrollbar */
2884 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2885 memset(&si, 0, sizeof(si));
2886 si.cbSize = sizeof(si);
2887 si.fMask = SIF_PAGE | SIF_RANGE;
2888 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2889 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2890 "Vertical scrollbar is invisible, should be visible.\n");
2891 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2892 "reported page/range is %d (%d..%d)\n",
2893 si.nPage, si.nMin, si.nMax);
2895 DISABLE_WS_VSCROLL(hwndRichEdit);
2896 memset(&si, 0, sizeof(si));
2897 si.cbSize = sizeof(si);
2898 si.fMask = SIF_PAGE | SIF_RANGE;
2899 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2900 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2901 "Vertical scrollbar is visible, should be invisible.\n");
2902 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2903 "reported page/range is %d (%d..%d)\n",
2904 si.nPage, si.nMin, si.nMax);
2906 /* Again, EM_SCROLL, with SB_LINEUP */
2907 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2908 memset(&si, 0, sizeof(si));
2909 si.cbSize = sizeof(si);
2910 si.fMask = SIF_PAGE | SIF_RANGE;
2911 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2912 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2913 "Vertical scrollbar is invisible, should be visible.\n");
2914 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2915 "reported page/range is %d (%d..%d)\n",
2916 si.nPage, si.nMin, si.nMax);
2918 DestroyWindow(hwndRichEdit);
2920 /* This window proc models what is going on with Corman Lisp 3.0.
2921 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
2922 force the scrollbar into visibility. Recursion should NOT happen
2923 as a result of this action.
2925 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
2927 richeditProc = cls.lpfnWndProc;
2928 cls.lpfnWndProc = RicheditStupidOverrideProcA;
2929 cls.lpszClassName = "RicheditStupidOverride";
2930 if(!RegisterClassA(&cls)) assert(0);
2933 WM_SIZE_recursionLevel = 0;
2934 bailedOutOfRecursion = FALSE;
2935 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
2936 ok(!bailedOutOfRecursion,
2937 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2940 WM_SIZE_recursionLevel = 0;
2941 bailedOutOfRecursion = FALSE;
2942 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
2943 ok(!bailedOutOfRecursion,
2944 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2946 /* Unblock window in order to process WM_DESTROY */
2948 bailedOutOfRecursion = FALSE;
2949 WM_SIZE_recursionLevel = 0;
2950 DestroyWindow(hwndRichEdit);
2954 static void test_EM_SETUNDOLIMIT(void)
2956 /* cases we test for:
2957 * default behaviour - limiting at 100 undo's
2958 * undo disabled - setting a limit of 0
2959 * undo limited - undo limit set to some to some number, like 2
2960 * bad input - sending a negative number should default to 100 undo's */
2962 HWND hwndRichEdit = new_richedit(NULL);
2967 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2970 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2971 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2972 also, multiple pastes don't combine like WM_CHAR would */
2973 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2975 /* first case - check the default */
2976 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2977 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2978 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2979 for (i=0; i<100; i++) /* Undo 100 of them */
2980 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2981 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2982 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
2984 /* second case - cannot undo */
2985 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2986 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
2987 SendMessage(hwndRichEdit,
2988 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
2989 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2990 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
2992 /* third case - set it to an arbitrary number */
2993 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2994 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
2995 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2996 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2997 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2998 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
2999 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3000 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3001 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3002 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3003 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3004 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3005 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3006 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3008 /* fourth case - setting negative numbers should default to 100 undos */
3009 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3010 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3012 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3014 DestroyWindow(hwndRichEdit);
3017 static void test_ES_PASSWORD(void)
3019 /* This isn't hugely testable, so we're just going to run it through its paces */
3021 HWND hwndRichEdit = new_richedit(NULL);
3024 /* First, check the default of a regular control */
3025 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3027 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3029 /* Now, set it to something normal */
3030 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3031 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3033 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3035 /* Now, set it to something odd */
3036 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3037 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3039 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3040 DestroyWindow(hwndRichEdit);
3043 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3048 char** str = (char**)dwCookie;
3051 memcpy(*str, pbBuff, *pcb);
3057 static void test_WM_SETTEXT()
3059 HWND hwndRichEdit = new_richedit(NULL);
3060 const char * TestItem1 = "TestSomeText";
3061 const char * TestItem2 = "TestSomeText\r";
3062 const char * TestItem2_after = "TestSomeText\r\n";
3063 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3064 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3065 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3066 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3067 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3068 const char * TestItem5_after = "TestSomeText TestSomeText";
3069 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3070 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3071 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3072 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3074 char buf[1024] = {0};
3079 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3080 any solitary \r to be converted to \r\n on return. Properly paired
3081 \r\n are not affected. It also shows that the special sequence \r\r\n
3082 gets converted to a single space.
3085 #define TEST_SETTEXT(a, b) \
3086 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3087 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3088 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3089 ok (result == lstrlen(buf), \
3090 "WM_GETTEXT returned %ld instead of expected %u\n", \
3091 result, lstrlen(buf)); \
3092 result = strcmp(b, buf); \
3094 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3096 TEST_SETTEXT(TestItem1, TestItem1)
3097 TEST_SETTEXT(TestItem2, TestItem2_after)
3098 TEST_SETTEXT(TestItem3, TestItem3_after)
3099 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3100 TEST_SETTEXT(TestItem4, TestItem4_after)
3101 TEST_SETTEXT(TestItem5, TestItem5_after)
3102 TEST_SETTEXT(TestItem6, TestItem6_after)
3103 TEST_SETTEXT(TestItem7, TestItem7_after)
3105 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3108 es.dwCookie = (DWORD_PTR)&p;
3110 es.pfnCallback = test_WM_SETTEXT_esCallback;
3111 memset(buf, 0, sizeof(buf));
3112 SendMessage(hwndRichEdit, EM_STREAMOUT,
3113 (WPARAM)(SF_RTF), (LPARAM)&es);
3114 trace("EM_STREAMOUT produced: \n%s\n", buf);
3115 TEST_SETTEXT(buf, TestItem1)
3118 DestroyWindow(hwndRichEdit);
3121 static void test_EM_STREAMOUT(void)
3123 HWND hwndRichEdit = new_richedit(NULL);
3126 char buf[1024] = {0};
3129 const char * TestItem1 = "TestSomeText";
3130 const char * TestItem2 = "TestSomeText\r";
3131 const char * TestItem3 = "TestSomeText\r\n";
3133 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3135 es.dwCookie = (DWORD_PTR)&p;
3137 es.pfnCallback = test_WM_SETTEXT_esCallback;
3138 memset(buf, 0, sizeof(buf));
3139 SendMessage(hwndRichEdit, EM_STREAMOUT,
3140 (WPARAM)(SF_TEXT), (LPARAM)&es);
3142 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3143 ok(strcmp(buf, TestItem1) == 0,
3144 "streamed text different, got %s\n", buf);
3146 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3148 es.dwCookie = (DWORD_PTR)&p;
3150 es.pfnCallback = test_WM_SETTEXT_esCallback;
3151 memset(buf, 0, sizeof(buf));
3152 SendMessage(hwndRichEdit, EM_STREAMOUT,
3153 (WPARAM)(SF_TEXT), (LPARAM)&es);
3155 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3156 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3157 ok(strcmp(buf, TestItem3) == 0,
3158 "streamed text different from, got %s\n", buf);
3159 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3161 es.dwCookie = (DWORD_PTR)&p;
3163 es.pfnCallback = test_WM_SETTEXT_esCallback;
3164 memset(buf, 0, sizeof(buf));
3165 SendMessage(hwndRichEdit, EM_STREAMOUT,
3166 (WPARAM)(SF_TEXT), (LPARAM)&es);
3168 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3169 ok(strcmp(buf, TestItem3) == 0,
3170 "streamed text different, got %s\n", buf);
3172 DestroyWindow(hwndRichEdit);
3175 static void test_EM_SETTEXTEX(void)
3177 HWND hwndRichEdit = new_richedit(NULL);
3180 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3182 'T', 'e', 'x', 't', 0};
3183 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3189 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3193 const char * TestItem2_after = "TestSomeText\r\n";
3194 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3197 '\r','\n','\r','\n', 0};
3198 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3202 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3206 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3209 '\r','\r','\n','\r',
3211 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3215 #define MAX_BUF_LEN 1024
3216 WCHAR buf[MAX_BUF_LEN];
3217 char bufACP[MAX_BUF_LEN];
3223 setText.codepage = 1200; /* no constant for unicode */
3224 getText.codepage = 1200; /* no constant for unicode */
3225 getText.cb = MAX_BUF_LEN;
3226 getText.flags = GT_DEFAULT;
3227 getText.lpDefaultChar = NULL;
3228 getText.lpUsedDefChar = NULL;
3231 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3232 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3233 ok(lstrcmpW(buf, TestItem1) == 0,
3234 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3236 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3237 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3239 setText.codepage = 1200; /* no constant for unicode */
3240 getText.codepage = 1200; /* no constant for unicode */
3241 getText.cb = MAX_BUF_LEN;
3242 getText.flags = GT_DEFAULT;
3243 getText.lpDefaultChar = NULL;
3244 getText.lpUsedDefChar = NULL;
3246 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3247 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3248 ok(lstrcmpW(buf, TestItem2) == 0,
3249 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3251 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3252 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3253 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3254 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3256 /* Baseline test for just-enough buffer space for string */
3257 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3258 getText.codepage = 1200; /* no constant for unicode */
3259 getText.flags = GT_DEFAULT;
3260 getText.lpDefaultChar = NULL;
3261 getText.lpUsedDefChar = NULL;
3262 memset(buf, 0, MAX_BUF_LEN);
3263 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3264 ok(lstrcmpW(buf, TestItem2) == 0,
3265 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3267 /* When there is enough space for one character, but not both, of the CRLF
3268 pair at the end of the string, the CR is not copied at all. That is,
3269 the caller must not see CRLF pairs truncated to CR at the end of the
3272 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3273 getText.codepage = 1200; /* no constant for unicode */
3274 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3275 getText.lpDefaultChar = NULL;
3276 getText.lpUsedDefChar = NULL;
3277 memset(buf, 0, MAX_BUF_LEN);
3278 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3279 ok(lstrcmpW(buf, TestItem1) == 0,
3280 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3283 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3284 setText.codepage = 1200; /* no constant for unicode */
3285 getText.codepage = 1200; /* no constant for unicode */
3286 getText.cb = MAX_BUF_LEN;
3287 getText.flags = GT_DEFAULT;
3288 getText.lpDefaultChar = NULL;
3289 getText.lpUsedDefChar = NULL;
3291 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3292 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3293 ok(lstrcmpW(buf, TestItem3_after) == 0,
3294 "EM_SETTEXTEX did not convert properly\n");
3296 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3297 setText.codepage = 1200; /* no constant for unicode */
3298 getText.codepage = 1200; /* no constant for unicode */
3299 getText.cb = MAX_BUF_LEN;
3300 getText.flags = GT_DEFAULT;
3301 getText.lpDefaultChar = NULL;
3302 getText.lpUsedDefChar = NULL;
3304 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3305 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3306 ok(lstrcmpW(buf, TestItem3_after) == 0,
3307 "EM_SETTEXTEX did not convert properly\n");
3309 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3310 setText.codepage = 1200; /* no constant for unicode */
3311 getText.codepage = 1200; /* no constant for unicode */
3312 getText.cb = MAX_BUF_LEN;
3313 getText.flags = GT_DEFAULT;
3314 getText.lpDefaultChar = NULL;
3315 getText.lpUsedDefChar = NULL;
3317 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3318 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3319 ok(lstrcmpW(buf, TestItem4_after) == 0,
3320 "EM_SETTEXTEX did not convert properly\n");
3322 /* !ST_SELECTION && Unicode && !\rtf */
3323 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3324 (WPARAM)&setText, (LPARAM) NULL);
3325 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3328 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3329 ok(lstrlenW(buf) == 0,
3330 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3332 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3334 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3335 /* select some text */
3338 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3339 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3340 setText.flags = ST_SELECTION;
3341 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3342 (WPARAM)&setText, (LPARAM) NULL);
3344 "EM_SETTEXTEX with NULL lParam to replace selection"
3345 " with no text should return 0. Got %i\n",
3348 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3350 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3351 /* select some text */
3354 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3355 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3356 setText.flags = ST_SELECTION;
3357 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3358 (WPARAM)&setText, (LPARAM) TestItem1);
3360 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3361 ok(result == lstrlenW(TestItem1),
3362 "EM_SETTEXTEX with NULL lParam to replace selection"
3363 " with no text should return 0. Got %i\n",
3365 ok(lstrlenW(buf) == 22,
3366 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3369 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3370 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3372 es.dwCookie = (DWORD_PTR)&p;
3374 es.pfnCallback = test_WM_SETTEXT_esCallback;
3375 memset(buf, 0, sizeof(buf));
3376 SendMessage(hwndRichEdit, EM_STREAMOUT,
3377 (WPARAM)(SF_RTF), (LPARAM)&es);
3378 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3380 /* !ST_SELECTION && !Unicode && \rtf */
3381 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3382 getText.codepage = 1200; /* no constant for unicode */
3383 getText.cb = MAX_BUF_LEN;
3384 getText.flags = GT_DEFAULT;
3385 getText.lpDefaultChar = NULL;
3386 getText.lpUsedDefChar = NULL;
3389 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3390 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3391 ok(lstrcmpW(buf, TestItem1) == 0,
3392 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3394 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3395 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3397 es.dwCookie = (DWORD_PTR)&p;
3399 es.pfnCallback = test_WM_SETTEXT_esCallback;
3400 memset(buf, 0, sizeof(buf));
3401 SendMessage(hwndRichEdit, EM_STREAMOUT,
3402 (WPARAM)(SF_RTF), (LPARAM)&es);
3403 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3405 /* select some text */
3408 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3410 /* ST_SELECTION && !Unicode && \rtf */
3411 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3412 getText.codepage = 1200; /* no constant for unicode */
3413 getText.cb = MAX_BUF_LEN;
3414 getText.flags = GT_DEFAULT;
3415 getText.lpDefaultChar = NULL;
3416 getText.lpUsedDefChar = NULL;
3418 setText.flags = ST_SELECTION;
3419 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3420 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3421 ok(lstrcmpW(buf, TestItem1alt) == 0,
3422 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3423 " using ST_SELECTION on an RTF string and non-Unicode\n");
3425 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3426 setText.codepage = 1200; /* no constant for unicode */
3427 getText.codepage = CP_ACP;
3428 getText.cb = MAX_BUF_LEN;
3431 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3432 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3434 /* select some text */
3437 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3439 /* ST_SELECTION && !Unicode && !\rtf */
3440 setText.codepage = CP_ACP;
3441 getText.codepage = 1200; /* no constant for unicode */
3442 getText.cb = MAX_BUF_LEN;
3443 getText.flags = GT_DEFAULT;
3444 getText.lpDefaultChar = NULL;
3445 getText.lpUsedDefChar = NULL;
3447 setText.flags = ST_SELECTION;
3448 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3449 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3450 ok(lstrcmpW(buf, TestItem1alt) == 0,
3451 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3452 " using ST_SELECTION and non-Unicode\n");
3455 DestroyWindow(hwndRichEdit);
3458 static void test_EM_LIMITTEXT(void)
3462 HWND hwndRichEdit = new_richedit(NULL);
3464 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3465 * about setting the length to -1 for multiline edit controls doesn't happen.
3468 /* Don't check default gettextlimit case. That's done in other tests */
3470 /* Set textlimit to 100 */
3471 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3472 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3474 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3476 /* Set textlimit to 0 */
3477 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3478 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3480 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3482 /* Set textlimit to -1 */
3483 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3484 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3486 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3488 /* Set textlimit to -2 */
3489 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3490 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3492 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3494 DestroyWindow (hwndRichEdit);
3498 static void test_EM_EXLIMITTEXT(void)
3500 int i, selBegin, selEnd, len1, len2;
3502 char text[1024 + 1];
3503 char buffer[1024 + 1];
3504 int textlimit = 0; /* multiple of 100 */
3505 HWND hwndRichEdit = new_richedit(NULL);
3507 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3508 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3511 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3512 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3514 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3517 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3518 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3520 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3522 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3523 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3524 /* default for WParam = 0 */
3525 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3527 textlimit = sizeof(text)-1;
3528 memset(text, 'W', textlimit);
3529 text[sizeof(text)-1] = 0;
3530 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3531 /* maxed out text */
3532 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3534 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3535 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3536 len1 = selEnd - selBegin;
3538 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3539 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3540 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3541 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3542 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3543 len2 = selEnd - selBegin;
3546 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3549 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3550 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3551 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3552 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3553 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3554 len1 = selEnd - selBegin;
3557 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3560 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3561 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3562 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3563 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3564 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3565 len2 = selEnd - selBegin;
3568 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3571 /* set text up to the limit, select all the text, then add a char */
3573 memset(text, 'W', textlimit);
3574 text[textlimit] = 0;
3575 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3576 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3577 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3578 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3579 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3580 result = strcmp(buffer, "A");
3581 ok(0 == result, "got string = \"%s\"\n", buffer);
3583 /* WM_SETTEXT not limited */
3585 memset(text, 'W', textlimit);
3586 text[textlimit] = 0;
3587 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3588 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3589 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3591 ok(10 == i, "expected 10 chars\n");
3592 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3593 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3595 /* try inserting more text at end */
3596 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3597 ok(0 == i, "WM_CHAR wasn't processed\n");
3598 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3600 ok(10 == i, "expected 10 chars, got %i\n", i);
3601 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3602 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3604 /* try inserting text at beginning */
3605 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3606 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3607 ok(0 == i, "WM_CHAR wasn't processed\n");
3608 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3610 ok(10 == i, "expected 10 chars, got %i\n", i);
3611 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3612 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3614 /* WM_CHAR is limited */
3616 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3617 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3618 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3619 ok(0 == i, "WM_CHAR wasn't processed\n");
3620 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3621 ok(0 == i, "WM_CHAR wasn't processed\n");
3622 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3624 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3626 DestroyWindow(hwndRichEdit);
3629 static void test_EM_GETLIMITTEXT(void)
3632 HWND hwndRichEdit = new_richedit(NULL);
3634 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3635 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3637 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3638 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3639 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3641 DestroyWindow(hwndRichEdit);
3644 static void test_WM_SETFONT(void)
3646 /* There is no invalid input or error conditions for this function.
3647 * NULL wParam and lParam just fall back to their default values
3648 * It should be noted that even if you use a gibberish name for your fonts
3649 * here, it will still work because the name is stored. They will display as
3650 * System, but will report their name to be whatever they were created as */
3652 HWND hwndRichEdit = new_richedit(NULL);
3653 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3654 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3655 FF_DONTCARE, "Marlett");
3656 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3657 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3658 FF_DONTCARE, "MS Sans Serif");
3659 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3660 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3661 FF_DONTCARE, "Courier");
3662 LOGFONTA sentLogFont;
3663 CHARFORMAT2A returnedCF2A;
3665 returnedCF2A.cbSize = sizeof(returnedCF2A);
3667 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3668 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
3669 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3671 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3672 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3673 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3674 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3676 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
3677 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3678 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3679 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3680 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3681 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3683 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
3684 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3685 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3686 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3687 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3688 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3690 /* This last test is special since we send in NULL. We clear the variables
3691 * and just compare to "System" instead of the sent in font name. */
3692 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3693 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3694 returnedCF2A.cbSize = sizeof(returnedCF2A);
3696 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
3697 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3698 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3699 ok (!strcmp("System",returnedCF2A.szFaceName),
3700 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3702 DestroyWindow(hwndRichEdit);
3706 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3711 const char** str = (const char**)dwCookie;
3712 int size = strlen(*str);
3713 if(size > 3) /* let's make it piecemeal for fun */
3720 memcpy(pbBuff, *str, *pcb);
3726 static void test_EM_GETMODIFY(void)
3728 HWND hwndRichEdit = new_richedit(NULL);
3731 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3733 'T', 'e', 'x', 't', 0};
3734 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3736 'O', 't', 'h', 'e', 'r',
3737 'T', 'e', 'x', 't', 0};
3738 const char* streamText = "hello world";
3743 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3744 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3745 FF_DONTCARE, "Courier");
3747 setText.codepage = 1200; /* no constant for unicode */
3748 setText.flags = ST_KEEPUNDO;
3751 /* modify flag shouldn't be set when richedit is first created */
3752 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3754 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
3756 /* setting modify flag should actually set it */
3757 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
3758 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3760 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
3762 /* clearing modify flag should actually clear it */
3763 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3764 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3766 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
3768 /* setting font doesn't change modify flag */
3769 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3770 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
3771 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3773 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
3775 /* setting text should set modify flag */
3776 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3777 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3778 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3780 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
3782 /* undo previous text doesn't reset modify flag */
3783 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3784 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3786 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
3788 /* set text with no flag to keep undo stack should not set modify flag */
3789 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3791 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3792 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3794 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
3796 /* WM_SETTEXT doesn't modify */
3797 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3798 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3799 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3801 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3803 /* clear the text */
3804 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3805 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3806 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3808 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3811 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3812 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3813 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3814 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3815 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3817 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3819 /* copy/paste text 1 */
3820 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3821 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3822 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3823 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3824 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3826 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3828 /* copy/paste text 2 */
3829 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3830 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3831 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3832 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3833 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3834 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3836 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3839 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3840 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3841 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3842 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3844 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3847 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3848 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3849 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
3850 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3852 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3854 /* set char format */
3855 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3856 cf2.cbSize = sizeof(CHARFORMAT2);
3857 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3859 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3860 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3861 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3862 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3863 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3864 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3866 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3868 /* set para format */
3869 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3870 pf2.cbSize = sizeof(PARAFORMAT2);
3871 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3873 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3874 pf2.wAlignment = PFA_RIGHT;
3875 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3876 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3878 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3881 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3882 es.dwCookie = (DWORD_PTR)&streamText;
3884 es.pfnCallback = test_EM_GETMODIFY_esCallback;
3885 SendMessage(hwndRichEdit, EM_STREAMIN,
3886 (WPARAM)(SF_TEXT), (LPARAM)&es);
3887 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3889 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
3891 DestroyWindow(hwndRichEdit);
3897 long expected_retval;
3898 int expected_getsel_start;
3899 int expected_getsel_end;
3900 int _exsetsel_todo_wine;
3901 int _getsel_todo_wine;
3904 const struct exsetsel_s exsetsel_tests[] = {
3906 {5, 10, 10, 5, 10, 0, 0},
3907 {15, 17, 17, 15, 17, 0, 0},
3908 /* test cpMax > strlen() */
3909 {0, 100, 18, 0, 18, 0, 1},
3910 /* test cpMin == cpMax */
3911 {5, 5, 5, 5, 5, 0, 0},
3912 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
3913 {-1, 0, 5, 5, 5, 0, 0},
3914 {-1, 17, 5, 5, 5, 0, 0},
3915 {-1, 18, 5, 5, 5, 0, 0},
3916 /* test cpMin < 0 && cpMax < 0 */
3917 {-1, -1, 17, 17, 17, 0, 0},
3918 {-4, -5, 17, 17, 17, 0, 0},
3919 /* test cMin >=0 && cpMax < 0 (bug 6814) */
3920 {0, -1, 18, 0, 18, 0, 1},
3921 {17, -5, 18, 17, 18, 0, 1},
3922 {18, -3, 17, 17, 17, 0, 0},
3923 /* test if cpMin > cpMax */
3924 {15, 19, 18, 15, 18, 0, 1},
3925 {19, 15, 18, 15, 18, 0, 1}
3928 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
3933 cr.cpMin = setsel->min;
3934 cr.cpMax = setsel->max;
3935 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
3937 if (setsel->_exsetsel_todo_wine) {
3939 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3942 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3945 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
3947 if (setsel->_getsel_todo_wine) {
3949 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);
3952 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);
3956 static void test_EM_EXSETSEL(void)
3958 HWND hwndRichEdit = new_richedit(NULL);
3960 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
3962 /* sending some text to the window */
3963 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3964 /* 01234567890123456*/
3967 for (i = 0; i < num_tests; i++) {
3968 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
3971 DestroyWindow(hwndRichEdit);
3974 static void test_EM_REPLACESEL(int redraw)
3976 HWND hwndRichEdit = new_richedit(NULL);
3977 char buffer[1024] = {0};
3982 /* sending some text to the window */
3983 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3984 /* 01234567890123456*/
3987 /* FIXME add more tests */
3988 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
3989 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
3990 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
3991 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3992 r = strcmp(buffer, "testing");
3993 ok(0 == r, "expected %d, got %d\n", 0, r);
3995 DestroyWindow(hwndRichEdit);
3997 hwndRichEdit = new_richedit(NULL);
3999 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4000 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4002 /* Test behavior with carriage returns and newlines */
4003 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4004 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4005 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4006 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4007 r = strcmp(buffer, "RichEdit1");
4008 ok(0 == r, "expected %d, got %d\n", 0, r);
4010 getText.codepage = CP_ACP;
4011 getText.flags = GT_DEFAULT;
4012 getText.lpDefaultChar = NULL;
4013 getText.lpUsedDefChar = NULL;
4014 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4015 ok(strcmp(buffer, "RichEdit1") == 0,
4016 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4018 /* Test number of lines reported after EM_REPLACESEL */
4019 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4020 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4022 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4023 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4024 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4025 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4026 r = strcmp(buffer, "RichEdit1\r\n");
4027 ok(0 == r, "expected %d, got %d\n", 0, r);
4029 getText.codepage = CP_ACP;
4030 getText.flags = GT_DEFAULT;
4031 getText.lpDefaultChar = NULL;
4032 getText.lpUsedDefChar = NULL;
4033 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4034 ok(strcmp(buffer, "RichEdit1\r") == 0,
4035 "EM_GETTEXTEX returned incorrect string\n");
4037 /* Test number of lines reported after EM_REPLACESEL */
4038 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4039 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4041 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4042 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4043 returns the number of characters *inserted* into the control (after
4044 required conversions), but WinXP's riched20 returns the number of
4045 characters interpreted from the original lParam. Wine's builtin riched20
4046 implements the WinXP behavior.
4048 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4049 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4050 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4051 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4053 /* Test number of lines reported after EM_REPLACESEL */
4054 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4055 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4057 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4058 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4059 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4060 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4062 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4063 r = strcmp(buffer, "RichEdit1\r\n");
4064 ok(0 == r, "expected %d, got %d\n", 0, r);
4066 getText.codepage = CP_ACP;
4067 getText.flags = GT_DEFAULT;
4068 getText.lpDefaultChar = NULL;
4069 getText.lpUsedDefChar = NULL;
4070 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4071 ok(strcmp(buffer, "RichEdit1\r") == 0,
4072 "EM_GETTEXTEX returned incorrect string\n");
4074 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4075 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4076 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4077 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4079 /* The following tests show that richedit should handle the special \r\r\n
4080 sequence by turning it into a single space on insertion. However,
4081 EM_REPLACESEL on WinXP returns the number of characters in the original
4085 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4086 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4087 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4088 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4089 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4090 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4091 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4093 /* Test the actual string */
4095 getText.codepage = CP_ACP;
4096 getText.flags = GT_DEFAULT;
4097 getText.lpDefaultChar = NULL;
4098 getText.lpUsedDefChar = NULL;
4099 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4100 ok(strcmp(buffer, "\r\r") == 0,
4101 "EM_GETTEXTEX returned incorrect string\n");
4103 /* Test number of lines reported after EM_REPLACESEL */
4104 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4105 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4107 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4108 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4109 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4110 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4111 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4112 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4113 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4114 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4116 /* Test the actual string */
4118 getText.codepage = CP_ACP;
4119 getText.flags = GT_DEFAULT;
4120 getText.lpDefaultChar = NULL;
4121 getText.lpUsedDefChar = NULL;
4122 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4123 ok(strcmp(buffer, " ") == 0,
4124 "EM_GETTEXTEX returned incorrect string\n");
4126 /* Test number of lines reported after EM_REPLACESEL */
4127 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4128 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4130 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4131 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4132 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4133 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4134 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4135 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4136 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4137 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4139 /* Test the actual string */
4141 getText.codepage = CP_ACP;
4142 getText.flags = GT_DEFAULT;
4143 getText.lpDefaultChar = NULL;
4144 getText.lpUsedDefChar = NULL;
4145 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4146 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4147 "EM_GETTEXTEX returned incorrect string\n");
4149 /* Test number of lines reported after EM_REPLACESEL */
4150 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4151 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4153 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4154 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4155 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4156 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4157 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4158 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4159 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4160 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4162 /* Test the actual string */
4164 getText.codepage = CP_ACP;
4165 getText.flags = GT_DEFAULT;
4166 getText.lpDefaultChar = NULL;
4167 getText.lpUsedDefChar = NULL;
4168 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4169 ok(strcmp(buffer, " \r") == 0,
4170 "EM_GETTEXTEX returned incorrect string\n");
4172 /* Test number of lines reported after EM_REPLACESEL */
4173 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4174 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4176 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4177 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4178 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4179 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4180 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4181 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4182 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4183 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4185 /* Test the actual string */
4187 getText.codepage = CP_ACP;
4188 getText.flags = GT_DEFAULT;
4189 getText.lpDefaultChar = NULL;
4190 getText.lpUsedDefChar = NULL;
4191 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4192 ok(strcmp(buffer, " \r\r") == 0,
4193 "EM_GETTEXTEX returned incorrect string\n");
4195 /* Test number of lines reported after EM_REPLACESEL */
4196 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4197 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4199 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4200 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4201 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4202 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4203 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4204 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4205 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4206 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4208 /* Test the actual string */
4210 getText.codepage = CP_ACP;
4211 getText.flags = GT_DEFAULT;
4212 getText.lpDefaultChar = NULL;
4213 getText.lpUsedDefChar = NULL;
4214 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4215 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4216 "EM_GETTEXTEX returned incorrect string\n");
4218 /* Test number of lines reported after EM_REPLACESEL */
4219 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4220 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4222 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4223 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4224 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4225 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4226 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4227 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4228 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4230 /* Test the actual string */
4232 getText.codepage = CP_ACP;
4233 getText.flags = GT_DEFAULT;
4234 getText.lpDefaultChar = NULL;
4235 getText.lpUsedDefChar = NULL;
4236 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4237 ok(strcmp(buffer, "\r\r") == 0,
4238 "EM_GETTEXTEX returned incorrect string\n");
4240 /* Test number of lines reported after EM_REPLACESEL */
4241 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4242 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4244 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4245 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4246 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4247 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4248 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4249 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4250 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4251 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4253 /* Test the actual string */
4255 getText.codepage = CP_ACP;
4256 getText.flags = GT_DEFAULT;
4257 getText.lpDefaultChar = NULL;
4258 getText.lpUsedDefChar = NULL;
4259 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4260 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4261 "EM_GETTEXTEX returned incorrect string\n");
4263 /* Test number of lines reported after EM_REPLACESEL */
4264 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4265 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4268 /* This is needed to avoid interferring with keybd_event calls
4269 * on other tests that simulate keyboard events. */
4270 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4272 DestroyWindow(hwndRichEdit);
4275 static void test_WM_PASTE(void)
4278 char buffer[1024] = {0};
4279 const char* text1 = "testing paste\r";
4280 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4281 const char* text1_after = "testing paste\r\n";
4282 const char* text2 = "testing paste\r\rtesting paste";
4283 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4284 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4285 HWND hwndRichEdit = new_richedit(NULL);
4287 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
4288 messages, probably because it inspects the keyboard state itself.
4289 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
4292 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4293 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4294 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4295 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4296 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4298 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4299 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4301 SEND_CTRL_C(hwndRichEdit); /* Copy */
4302 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4303 SEND_CTRL_V(hwndRichEdit); /* Paste */
4304 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4305 /* Pasted text should be visible at this step */
4306 result = strcmp(text1_step1, buffer);
4308 "test paste: strcmp = %i\n", result);
4309 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4310 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4311 /* Text should be the same as before (except for \r -> \r\n conversion) */
4312 result = strcmp(text1_after, buffer);
4314 "test paste: strcmp = %i\n", result);
4316 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4317 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4318 SEND_CTRL_C(hwndRichEdit); /* Copy */
4319 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4320 SEND_CTRL_V(hwndRichEdit); /* Paste */
4321 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4322 /* Pasted text should be visible at this step */
4323 result = strcmp(text3, buffer);
4325 "test paste: strcmp = %i\n", result);
4326 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4327 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4328 /* Text should be the same as before (except for \r -> \r\n conversion) */
4329 result = strcmp(text2_after, buffer);
4331 "test paste: strcmp = %i\n", result);
4332 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4333 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4334 /* Text should revert to post-paste state */
4335 result = strcmp(buffer,text3);
4337 "test paste: strcmp = %i\n", result);
4339 DestroyWindow(hwndRichEdit);
4342 static void test_EM_FORMATRANGE(void)
4347 HWND hwndRichEdit = new_richedit(NULL);
4349 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4351 hdc = GetDC(hwndRichEdit);
4352 ok(hdc != NULL, "Could not get HDC\n");
4354 fr.hdc = fr.hdcTarget = hdc;
4355 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4356 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4357 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4361 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4363 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4366 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4368 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
4374 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4376 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4379 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4381 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4384 DestroyWindow(hwndRichEdit);
4387 static int nCallbackCount = 0;
4389 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4392 const char text[] = {'t','e','s','t'};
4394 if (sizeof(text) <= cb)
4396 if ((int)dwCookie != nCallbackCount)
4402 memcpy (pbBuff, text, sizeof(text));
4403 *pcb = sizeof(text);
4410 return 1; /* indicates callback failed */
4413 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4418 const char** str = (const char**)dwCookie;
4419 int size = strlen(*str);
4425 memcpy(pbBuff, *str, *pcb);
4431 struct StringWithLength {
4436 /* This callback is used to handled the null characters in a string. */
4437 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4442 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4443 int size = str->length;
4449 memcpy(pbBuff, str->buffer, *pcb);
4450 str->buffer += *pcb;
4451 str->length -= *pcb;
4456 static void test_EM_STREAMIN(void)
4458 HWND hwndRichEdit = new_richedit(NULL);
4461 char buffer[1024] = {0};
4463 const char * streamText0 = "{\\rtf1 TestSomeText}";
4464 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4465 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4467 const char * streamText1 =
4468 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
4469 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
4472 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4473 const char * streamText2 =
4474 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
4475 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
4476 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
4477 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
4478 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
4479 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
4480 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4482 const char * streamText3 = "RichEdit1";
4484 struct StringWithLength cookieForStream4;
4485 const char * streamText4 =
4486 "This text just needs to be long enough to cause run to be split onto "\
4487 "two separate lines and make sure the null terminating character is "\
4488 "handled properly.\0";
4489 int length4 = strlen(streamText4) + 1;
4490 cookieForStream4.buffer = (char *)streamText4;
4491 cookieForStream4.length = length4;
4493 /* Minimal test without \par at the end */
4494 es.dwCookie = (DWORD_PTR)&streamText0;
4496 es.pfnCallback = test_EM_STREAMIN_esCallback;
4497 SendMessage(hwndRichEdit, EM_STREAMIN,
4498 (WPARAM)(SF_RTF), (LPARAM)&es);
4500 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4502 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4503 result = strcmp (buffer,"TestSomeText");
4505 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4506 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4508 /* Native richedit 2.0 ignores last \par */
4509 es.dwCookie = (DWORD_PTR)&streamText0a;
4511 es.pfnCallback = test_EM_STREAMIN_esCallback;
4512 SendMessage(hwndRichEdit, EM_STREAMIN,
4513 (WPARAM)(SF_RTF), (LPARAM)&es);
4515 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4517 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4518 result = strcmp (buffer,"TestSomeText");
4520 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4521 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4523 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4524 es.dwCookie = (DWORD_PTR)&streamText0b;
4526 es.pfnCallback = test_EM_STREAMIN_esCallback;
4527 SendMessage(hwndRichEdit, EM_STREAMIN,
4528 (WPARAM)(SF_RTF), (LPARAM)&es);
4530 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4532 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4533 result = strcmp (buffer,"TestSomeText\r\n");
4535 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4536 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4538 es.dwCookie = (DWORD_PTR)&streamText1;
4540 es.pfnCallback = test_EM_STREAMIN_esCallback;
4541 SendMessage(hwndRichEdit, EM_STREAMIN,
4542 (WPARAM)(SF_RTF), (LPARAM)&es);
4544 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4546 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4547 result = strcmp (buffer,"TestSomeText");
4549 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4550 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4552 es.dwCookie = (DWORD_PTR)&streamText2;
4554 SendMessage(hwndRichEdit, EM_STREAMIN,
4555 (WPARAM)(SF_RTF), (LPARAM)&es);
4557 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4559 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4560 ok (strlen(buffer) == 0,
4561 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4562 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4564 es.dwCookie = (DWORD_PTR)&streamText3;
4566 SendMessage(hwndRichEdit, EM_STREAMIN,
4567 (WPARAM)(SF_RTF), (LPARAM)&es);
4569 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4571 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4572 ok (strlen(buffer) == 0,
4573 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4574 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4576 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4578 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4579 SendMessage(hwndRichEdit, EM_STREAMIN,
4580 (WPARAM)(SF_TEXT), (LPARAM)&es);
4582 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4583 ok (result == length4,
4584 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4585 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4587 DestroyWindow(hwndRichEdit);
4590 static void test_EM_StreamIn_Undo(void)
4592 /* The purpose of this test is to determine when a EM_StreamIn should be
4593 * undoable. This is important because WM_PASTE currently uses StreamIn and
4594 * pasting should always be undoable but streaming isn't always.
4597 * StreamIn plain text without SFF_SELECTION.
4598 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4599 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4600 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4601 * Feel free to add tests for other text modes or StreamIn things.
4605 HWND hwndRichEdit = new_richedit(NULL);
4608 char buffer[1024] = {0};
4609 const char randomtext[] = "Some text";
4611 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4613 /* StreamIn, no SFF_SELECTION */
4614 es.dwCookie = nCallbackCount;
4615 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4616 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4617 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4618 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4619 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4620 result = strcmp (buffer,"test");
4622 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4624 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4625 ok (result == FALSE,
4626 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4628 /* StreamIn, SFF_SELECTION, but nothing selected */
4629 es.dwCookie = nCallbackCount;
4630 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4631 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4632 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4633 SendMessage(hwndRichEdit, EM_STREAMIN,
4634 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4635 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4636 result = strcmp (buffer,"testSome text");
4638 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4640 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4642 "EM_STREAMIN with SFF_SELECTION but no selection set "
4643 "should create an undo\n");
4645 /* StreamIn, SFF_SELECTION, with a selection */
4646 es.dwCookie = nCallbackCount;
4647 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4648 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4649 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
4650 SendMessage(hwndRichEdit, EM_STREAMIN,
4651 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4652 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4653 result = strcmp (buffer,"Sometesttext");
4655 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4657 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4659 "EM_STREAMIN with SFF_SELECTION and selection set "
4660 "should create an undo\n");
4662 DestroyWindow(hwndRichEdit);
4665 static BOOL is_em_settextex_supported(HWND hwnd)
4667 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
4668 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
4671 static void test_unicode_conversions(void)
4673 static const WCHAR tW[] = {'t',0};
4674 static const WCHAR teW[] = {'t','e',0};
4675 static const WCHAR textW[] = {'t','e','s','t',0};
4676 static const char textA[] = "test";
4680 int is_win9x, em_settextex_supported, ret;
4682 is_win9x = GetVersion() & 0x80000000;
4684 #define set_textA(hwnd, wm_set_text, txt) \
4686 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
4687 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4688 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4689 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4690 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
4692 #define expect_textA(hwnd, wm_get_text, txt) \
4694 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4695 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4696 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4697 memset(bufA, 0xAA, sizeof(bufA)); \
4698 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4699 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4700 ret = lstrcmpA(bufA, txt); \
4701 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
4704 #define set_textW(hwnd, wm_set_text, txt) \
4706 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
4707 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4708 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4709 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4710 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
4712 #define expect_textW(hwnd, wm_get_text, txt) \
4714 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
4715 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4716 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4717 memset(bufW, 0xAA, sizeof(bufW)); \
4720 assert(wm_get_text == EM_GETTEXTEX); \
4721 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4722 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4726 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4727 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
4729 ret = lstrcmpW(bufW, txt); \
4730 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
4732 #define expect_empty(hwnd, wm_get_text) \
4734 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4735 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4736 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4737 memset(bufA, 0xAA, sizeof(bufA)); \
4738 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4739 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
4740 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
4743 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4744 0, 0, 200, 60, 0, 0, 0, 0);
4745 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4747 ret = IsWindowUnicode(hwnd);
4749 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
4751 ok(ret, "RichEdit20W should be unicode under NT\n");
4753 /* EM_SETTEXTEX is supported starting from version 3.0 */
4754 em_settextex_supported = is_em_settextex_supported(hwnd);
4755 trace("EM_SETTEXTEX is %ssupported on this platform\n",
4756 em_settextex_supported ? "" : "NOT ");
4758 expect_empty(hwnd, WM_GETTEXT);
4759 expect_empty(hwnd, EM_GETTEXTEX);
4761 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
4762 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4763 expect_textA(hwnd, WM_GETTEXT, "t");
4764 expect_textA(hwnd, EM_GETTEXTEX, "t");
4765 expect_textW(hwnd, EM_GETTEXTEX, tW);
4767 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
4768 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4769 expect_textA(hwnd, WM_GETTEXT, "te");
4770 expect_textA(hwnd, EM_GETTEXTEX, "te");
4771 expect_textW(hwnd, EM_GETTEXTEX, teW);
4773 set_textA(hwnd, WM_SETTEXT, NULL);
4774 expect_empty(hwnd, WM_GETTEXT);
4775 expect_empty(hwnd, EM_GETTEXTEX);
4778 set_textA(hwnd, WM_SETTEXT, textW);
4780 set_textA(hwnd, WM_SETTEXT, textA);
4781 expect_textA(hwnd, WM_GETTEXT, textA);
4782 expect_textA(hwnd, EM_GETTEXTEX, textA);
4783 expect_textW(hwnd, EM_GETTEXTEX, textW);
4785 if (em_settextex_supported)
4787 set_textA(hwnd, EM_SETTEXTEX, textA);
4788 expect_textA(hwnd, WM_GETTEXT, textA);
4789 expect_textA(hwnd, EM_GETTEXTEX, textA);
4790 expect_textW(hwnd, EM_GETTEXTEX, textW);
4795 set_textW(hwnd, WM_SETTEXT, textW);
4796 expect_textW(hwnd, WM_GETTEXT, textW);
4797 expect_textA(hwnd, WM_GETTEXT, textA);
4798 expect_textW(hwnd, EM_GETTEXTEX, textW);
4799 expect_textA(hwnd, EM_GETTEXTEX, textA);
4801 if (em_settextex_supported)
4803 set_textW(hwnd, EM_SETTEXTEX, textW);
4804 expect_textW(hwnd, WM_GETTEXT, textW);
4805 expect_textA(hwnd, WM_GETTEXT, textA);
4806 expect_textW(hwnd, EM_GETTEXTEX, textW);
4807 expect_textA(hwnd, EM_GETTEXTEX, textA);
4810 DestroyWindow(hwnd);
4812 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
4813 0, 0, 200, 60, 0, 0, 0, 0);
4814 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4816 ret = IsWindowUnicode(hwnd);
4817 ok(!ret, "RichEdit20A should NOT be unicode\n");
4819 set_textA(hwnd, WM_SETTEXT, textA);
4820 expect_textA(hwnd, WM_GETTEXT, textA);
4821 expect_textA(hwnd, EM_GETTEXTEX, textA);
4822 expect_textW(hwnd, EM_GETTEXTEX, textW);
4824 if (em_settextex_supported)
4826 set_textA(hwnd, EM_SETTEXTEX, textA);
4827 expect_textA(hwnd, WM_GETTEXT, textA);
4828 expect_textA(hwnd, EM_GETTEXTEX, textA);
4829 expect_textW(hwnd, EM_GETTEXTEX, textW);
4834 set_textW(hwnd, WM_SETTEXT, textW);
4835 expect_textW(hwnd, WM_GETTEXT, textW);
4836 expect_textA(hwnd, WM_GETTEXT, textA);
4837 expect_textW(hwnd, EM_GETTEXTEX, textW);
4838 expect_textA(hwnd, EM_GETTEXTEX, textA);
4840 if (em_settextex_supported)
4842 set_textW(hwnd, EM_SETTEXTEX, textW);
4843 expect_textW(hwnd, WM_GETTEXT, textW);
4844 expect_textA(hwnd, WM_GETTEXT, textA);
4845 expect_textW(hwnd, EM_GETTEXTEX, textW);
4846 expect_textA(hwnd, EM_GETTEXTEX, textA);
4849 DestroyWindow(hwnd);
4852 static void test_WM_CHAR(void)
4856 const char * char_list = "abc\rabc\r";
4857 const char * expected_content_single = "abcabc";
4858 const char * expected_content_multi = "abc\r\nabc\r\n";
4859 char buffer[64] = {0};
4862 /* single-line control must IGNORE carriage returns */
4863 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4864 0, 0, 200, 60, 0, 0, 0, 0);
4865 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4868 while (*p != '\0') {
4869 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4870 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4871 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4872 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4876 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4877 ret = strcmp(buffer, expected_content_single);
4878 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4880 DestroyWindow(hwnd);
4882 /* multi-line control inserts CR normally */
4883 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4884 0, 0, 200, 60, 0, 0, 0, 0);
4885 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4888 while (*p != '\0') {
4889 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4890 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4891 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4892 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4896 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4897 ret = strcmp(buffer, expected_content_multi);
4898 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4900 DestroyWindow(hwnd);
4903 static void test_EM_GETTEXTLENGTHEX(void)
4906 GETTEXTLENGTHEX gtl;
4908 const char * base_string = "base string";
4909 const char * test_string = "a\nb\n\n\r\n";
4910 const char * test_string_after = "a";
4911 const char * test_string_2 = "a\rtest\rstring";
4912 char buffer[64] = {0};
4915 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4916 0, 0, 200, 60, 0, 0, 0, 0);
4917 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4919 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4920 gtl.codepage = CP_ACP;
4921 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4922 ok(ret == 0, "ret %d\n",ret);
4924 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4925 gtl.codepage = CP_ACP;
4926 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4927 ok(ret == 0, "ret %d\n",ret);
4929 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4931 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4932 gtl.codepage = CP_ACP;
4933 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4934 ok(ret == strlen(base_string), "ret %d\n",ret);
4936 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4937 gtl.codepage = CP_ACP;
4938 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4939 ok(ret == strlen(base_string), "ret %d\n",ret);
4941 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4943 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4944 gtl.codepage = CP_ACP;
4945 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4946 ok(ret == 1, "ret %d\n",ret);
4948 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4949 gtl.codepage = CP_ACP;
4950 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4951 ok(ret == 1, "ret %d\n",ret);
4953 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4954 ret = strcmp(buffer, test_string_after);
4955 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4957 DestroyWindow(hwnd);
4960 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
4961 0, 0, 200, 60, 0, 0, 0, 0);
4962 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4964 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4965 gtl.codepage = CP_ACP;
4966 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4967 ok(ret == 0, "ret %d\n",ret);
4969 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4970 gtl.codepage = CP_ACP;
4971 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4972 ok(ret == 0, "ret %d\n",ret);
4974 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4976 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4977 gtl.codepage = CP_ACP;
4978 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4979 ok(ret == strlen(base_string), "ret %d\n",ret);
4981 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4982 gtl.codepage = CP_ACP;
4983 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4984 ok(ret == strlen(base_string), "ret %d\n",ret);
4986 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
4988 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4989 gtl.codepage = CP_ACP;
4990 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4991 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
4993 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4994 gtl.codepage = CP_ACP;
4995 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4996 ok(ret == strlen(test_string_2), "ret %d\n",ret);
4998 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5000 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5001 gtl.codepage = CP_ACP;
5002 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5003 ok(ret == 10, "ret %d\n",ret);
5005 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5006 gtl.codepage = CP_ACP;
5007 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5008 ok(ret == 6, "ret %d\n",ret);
5010 DestroyWindow(hwnd);
5014 /* globals that parent and child access when checking event masks & notifications */
5015 static HWND eventMaskEditHwnd = 0;
5016 static int queriedEventMask;
5017 static int watchForEventMask = 0;
5019 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5020 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5022 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5024 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5026 return DefWindowProcA(hwnd, message, wParam, lParam);
5029 /* test event masks in combination with WM_COMMAND */
5030 static void test_eventMask(void)
5035 const char text[] = "foo bar\n";
5038 /* register class to capture WM_COMMAND */
5040 cls.lpfnWndProc = ParentMsgCheckProcA;
5043 cls.hInstance = GetModuleHandleA(0);
5045 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5046 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5047 cls.lpszMenuName = NULL;
5048 cls.lpszClassName = "EventMaskParentClass";
5049 if(!RegisterClassA(&cls)) assert(0);
5051 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5052 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5053 ok (parent != 0, "Failed to create parent window\n");
5055 eventMaskEditHwnd = new_richedit(parent);
5056 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5058 eventMask = ENM_CHANGE | ENM_UPDATE;
5059 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5060 ok(ret == ENM_NONE, "wrong event mask\n");
5061 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5062 ok(ret == eventMask, "failed to set event mask\n");
5064 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5065 queriedEventMask = 0; /* initialize to something other than we expect */
5066 watchForEventMask = EN_CHANGE;
5067 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5068 ok(ret == TRUE, "failed to set text\n");
5069 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5070 notification in response to WM_SETTEXT */
5071 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5072 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5076 static int received_WM_NOTIFY = 0;
5077 static int modify_at_WM_NOTIFY = 0;
5078 static HWND hwndRichedit_WM_NOTIFY;
5080 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5082 if(message == WM_NOTIFY)
5084 received_WM_NOTIFY = 1;
5085 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5087 return DefWindowProcA(hwnd, message, wParam, lParam);
5090 static void test_WM_NOTIFY(void)
5096 /* register class to capture WM_NOTIFY */
5098 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5101 cls.hInstance = GetModuleHandleA(0);
5103 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5104 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5105 cls.lpszMenuName = NULL;
5106 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5107 if(!RegisterClassA(&cls)) assert(0);
5109 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5110 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5111 ok (parent != 0, "Failed to create parent window\n");
5113 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5114 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5116 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5118 /* Notifications for selection change should only be sent when selection
5119 actually changes. EM_SETCHARFORMAT is one message that calls
5120 ME_CommitUndo, which should check whether message should be sent */
5121 received_WM_NOTIFY = 0;
5122 cf2.cbSize = sizeof(CHARFORMAT2);
5123 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5125 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5126 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5127 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5128 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5130 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5132 received_WM_NOTIFY = 0;
5133 modify_at_WM_NOTIFY = 0;
5134 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5135 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5136 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5138 received_WM_NOTIFY = 0;
5139 modify_at_WM_NOTIFY = 0;
5140 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5141 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5143 received_WM_NOTIFY = 0;
5144 modify_at_WM_NOTIFY = 0;
5145 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5146 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5147 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5149 DestroyWindow(hwndRichedit_WM_NOTIFY);
5150 DestroyWindow(parent);
5153 static void test_undo_coalescing(void)
5157 char buffer[64] = {0};
5159 /* multi-line control inserts CR normally */
5160 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5161 0, 0, 200, 60, 0, 0, 0, 0);
5162 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5164 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5165 ok (result == FALSE, "Can undo after window creation.\n");
5166 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5167 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5168 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5169 ok (result == FALSE, "Can redo after window creation.\n");
5170 result = SendMessage(hwnd, EM_REDO, 0, 0);
5171 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5173 /* Test the effect of arrows keys during typing on undo transactions*/
5174 simulate_typing_characters(hwnd, "one two three");
5175 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5176 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5177 simulate_typing_characters(hwnd, " four five six");
5179 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5180 ok (result == FALSE, "Can redo before anything is undone.\n");
5181 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5182 ok (result == TRUE, "Cannot undo typed characters.\n");
5183 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5184 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5185 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5186 ok (result == TRUE, "Cannot redo after undo.\n");
5187 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5188 result = strcmp(buffer, "one two three");
5189 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5191 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5192 ok (result == TRUE, "Cannot undo typed characters.\n");
5193 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5194 ok (result == TRUE, "Failed to undo typed characters.\n");
5195 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5196 result = strcmp(buffer, "");
5197 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5199 /* Test the effect of focus changes during typing on undo transactions*/
5200 simulate_typing_characters(hwnd, "one two three");
5201 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5202 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5203 SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
5204 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
5205 simulate_typing_characters(hwnd, " four five six");
5206 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5207 ok (result == TRUE, "Failed to undo typed characters.\n");
5208 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5209 result = strcmp(buffer, "one two three");
5210 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5212 /* Test the effect of the back key during typing on undo transactions */
5213 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5214 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5215 ok (result == TRUE, "Failed to clear the text.\n");
5216 simulate_typing_characters(hwnd, "one two threa");
5217 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5218 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5219 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5220 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5221 simulate_typing_characters(hwnd, "e four five six");
5222 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5223 ok (result == TRUE, "Failed to undo typed characters.\n");
5224 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5225 result = strcmp(buffer, "");
5226 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5228 /* Test the effect of the delete key during typing on undo transactions */
5229 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5230 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5231 ok(result == TRUE, "Failed to set the text.\n");
5232 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5233 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5234 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5235 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5236 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5237 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5238 ok (result == TRUE, "Failed to undo typed characters.\n");
5239 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5240 result = strcmp(buffer, "acd");
5241 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5242 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5243 ok (result == TRUE, "Failed to undo typed characters.\n");
5244 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5245 result = strcmp(buffer, "abcd");
5246 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5248 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5249 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5250 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5251 ok (result == TRUE, "Failed to clear the text.\n");
5252 simulate_typing_characters(hwnd, "one two three");
5253 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5254 ok (result == 0, "expected %d but got %d\n", 0, result);
5255 simulate_typing_characters(hwnd, " four five six");
5256 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5257 ok (result == TRUE, "Failed to undo typed characters.\n");
5258 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5259 result = strcmp(buffer, "one two three");
5260 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5261 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5262 ok (result == TRUE, "Failed to undo typed characters.\n");
5263 ok (result == TRUE, "Failed to undo typed characters.\n");
5264 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5265 result = strcmp(buffer, "");
5266 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5268 DestroyWindow(hwnd);
5271 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5272 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5274 static void test_word_movement(void)
5278 int sel_start, sel_end;
5280 /* multi-line control inserts CR normally */
5281 hwnd = new_richedit(NULL);
5283 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5284 ok (result == TRUE, "Failed to clear the text.\n");
5285 SendMessage(hwnd, EM_SETSEL, 0, 0);
5286 /* |one two three */
5288 SEND_CTRL_RIGHT(hwnd);
5289 /* one |two three */
5290 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5291 ok(sel_start == sel_end, "Selection should be empty\n");
5292 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5294 SEND_CTRL_RIGHT(hwnd);
5295 /* one two |three */
5296 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5297 ok(sel_start == sel_end, "Selection should be empty\n");
5298 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5300 SEND_CTRL_LEFT(hwnd);
5301 /* one |two three */
5302 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5303 ok(sel_start == sel_end, "Selection should be empty\n");
5304 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5306 SEND_CTRL_LEFT(hwnd);
5307 /* |one two three */
5308 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5309 ok(sel_start == sel_end, "Selection should be empty\n");
5310 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5312 SendMessage(hwnd, EM_SETSEL, 8, 8);
5313 /* one two | three */
5314 SEND_CTRL_RIGHT(hwnd);
5315 /* one two |three */
5316 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5317 ok(sel_start == sel_end, "Selection should be empty\n");
5318 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5320 SendMessage(hwnd, EM_SETSEL, 11, 11);
5321 /* one two th|ree */
5322 SEND_CTRL_LEFT(hwnd);
5323 /* one two |three */
5324 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5325 ok(sel_start == sel_end, "Selection should be empty\n");
5326 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5328 DestroyWindow(hwnd);
5331 static void test_EM_CHARFROMPOS(void)
5339 /* multi-line control inserts CR normally */
5340 hwnd = new_richedit(NULL);
5341 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5342 (LPARAM)"one two three four five six seven");
5344 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5345 ok(result == 0, "expected character index of 0 but got %d\n", result);
5347 DestroyWindow(hwnd);
5350 START_TEST( editor )
5355 /* Must explicitly LoadLibrary(). The test has no references to functions in
5356 * RICHED20.DLL, so the linker doesn't actually link to it. */
5357 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
5358 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
5362 test_EM_POSFROMCHAR();
5363 test_EM_SCROLLCARET();
5365 test_scrollbar_visibility();
5367 test_EM_LINELENGTH();
5368 test_EM_SETCHARFORMAT();
5369 test_EM_SETTEXTMODE();
5370 test_TM_PLAINTEXT();
5371 test_EM_SETOPTIONS();
5373 test_EM_GETTEXTRANGE();
5374 test_EM_GETSELTEXT();
5375 test_EM_SETUNDOLIMIT();
5377 test_EM_SETTEXTEX();
5378 test_EM_LIMITTEXT();
5379 test_EM_EXLIMITTEXT();
5380 test_EM_GETLIMITTEXT();
5382 test_EM_GETMODIFY();
5386 test_EM_STREAMOUT();
5387 test_EM_StreamIn_Undo();
5388 test_EM_FORMATRANGE();
5389 test_unicode_conversions();
5390 test_EM_GETTEXTLENGTHEX();
5391 test_EM_REPLACESEL(1);
5392 test_EM_REPLACESEL(0);
5394 test_EM_AUTOURLDETECT();
5396 test_undo_coalescing();
5397 test_word_movement();
5398 test_EM_CHARFROMPOS();
5400 /* Set the environment variable WINETEST_RICHED20 to keep windows
5401 * responsive and open for 30 seconds. This is useful for debugging.
5403 * The message pump uses PeekMessage() to empty the queue and then sleeps for
5404 * 50ms before retrying the queue. */
5405 end = time(NULL) + 30;
5406 if (getenv( "WINETEST_RICHED20" )) {
5407 while (time(NULL) < end) {
5408 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
5409 TranslateMessage(&msg);
5410 DispatchMessage(&msg);
5417 OleFlushClipboard();
5418 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());