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_SETPARAFORMAT(void)
1092 HWND hwndRichEdit = new_richedit(NULL);
1095 fmt.cbSize = sizeof(PARAFORMAT2);
1096 fmt.dwMask = PFM_ALIGNMENT;
1097 fmt.wAlignment = PFA_LEFT;
1099 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1100 ok(ret != 0, "expected non-zero got %d\n", ret);
1102 fmt.cbSize = sizeof(PARAFORMAT2);
1104 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1105 ok(ret == PFM_ALL2, "expected %x got %x\n", PFM_ALL2, ret);
1106 ok(fmt.dwMask == PFM_ALL2, "expected %x got %x\n", PFM_ALL2, fmt.dwMask);
1108 DestroyWindow(hwndRichEdit);
1111 static void test_TM_PLAINTEXT(void)
1113 /*Tests plain text properties*/
1115 HWND hwndRichEdit = new_richedit(NULL);
1116 CHARFORMAT2 cf2, cf2test;
1120 /*Switch to plain text mode*/
1122 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1123 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1125 /*Fill control with text*/
1127 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1129 /*Select some text and bold it*/
1133 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1134 cf2.cbSize = sizeof(CHARFORMAT2);
1135 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1138 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1139 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1141 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1142 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1144 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1145 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1147 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1148 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1150 /*Get the formatting of those characters*/
1152 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1154 /*Get the formatting of some other characters*/
1155 cf2test.cbSize = sizeof(CHARFORMAT2);
1158 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1159 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1161 /*Test that they are the same as plain text allows only one formatting*/
1163 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1164 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1165 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1167 /*Fill the control with a "wine" string, which when inserted will be bold*/
1169 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1171 /*Copy the bolded "wine" string*/
1175 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1176 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1178 /*Swap back to rich text*/
1180 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1181 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1183 /*Set the default formatting to bold italics*/
1185 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1186 cf2.dwMask |= CFM_ITALIC;
1187 cf2.dwEffects ^= CFE_ITALIC;
1188 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1189 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1191 /*Set the text in the control to "wine", which will be bold and italicized*/
1193 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1195 /*Paste the plain text "wine" string, which should take the insert
1196 formatting, which at the moment is bold italics*/
1198 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1200 /*Select the first "wine" string and retrieve its formatting*/
1204 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1205 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1207 /*Select the second "wine" string and retrieve its formatting*/
1211 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1212 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1214 /*Compare the two formattings. They should be the same.*/
1216 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1217 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1218 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1219 DestroyWindow(hwndRichEdit);
1222 static void test_WM_GETTEXT(void)
1224 HWND hwndRichEdit = new_richedit(NULL);
1225 static const char text[] = "Hello. My name is RichEdit!";
1226 static const char text2[] = "Hello. My name is RichEdit!\r";
1227 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1228 char buffer[1024] = {0};
1231 /* Baseline test with normal-sized buffer */
1232 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1233 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1234 ok(result == lstrlen(buffer),
1235 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1236 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1237 result = strcmp(buffer,text);
1239 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1241 /* Test for returned value of WM_GETTEXTLENGTH */
1242 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1243 ok(result == lstrlen(text),
1244 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1245 result, lstrlen(text));
1247 /* Test for behavior in overflow case */
1248 memset(buffer, 0, 1024);
1249 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1251 result == lstrlenA(text) - 1, /* XP, win2k3 */
1252 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1253 result = strcmp(buffer,text);
1255 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1257 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1259 /* Baseline test with normal-sized buffer and carriage return */
1260 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1261 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1262 ok(result == lstrlen(buffer),
1263 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1264 result = strcmp(buffer,text2_after);
1266 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1268 /* Test for returned value of WM_GETTEXTLENGTH */
1269 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1270 ok(result == lstrlen(text2_after),
1271 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1272 result, lstrlen(text2_after));
1274 /* Test for behavior of CRLF conversion in case of overflow */
1275 memset(buffer, 0, 1024);
1276 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1278 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1279 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1280 result = strcmp(buffer,text2);
1282 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1284 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1286 DestroyWindow(hwndRichEdit);
1289 static void test_EM_GETTEXTRANGE(void)
1291 HWND hwndRichEdit = new_richedit(NULL);
1292 const char * text1 = "foo bar\r\nfoo bar";
1293 const char * text2 = "foo bar\rfoo bar";
1294 const char * expect = "bar\rfoo";
1295 char buffer[1024] = {0};
1297 TEXTRANGEA textRange;
1299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1301 textRange.lpstrText = buffer;
1302 textRange.chrg.cpMin = 4;
1303 textRange.chrg.cpMax = 11;
1304 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1305 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1306 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1308 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1310 textRange.lpstrText = buffer;
1311 textRange.chrg.cpMin = 4;
1312 textRange.chrg.cpMax = 11;
1313 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1314 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1315 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1317 DestroyWindow(hwndRichEdit);
1320 static void test_EM_GETSELTEXT(void)
1322 HWND hwndRichEdit = new_richedit(NULL);
1323 const char * text1 = "foo bar\r\nfoo bar";
1324 const char * text2 = "foo bar\rfoo bar";
1325 const char * expect = "bar\rfoo";
1326 char buffer[1024] = {0};
1329 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1331 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1332 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1333 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1334 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1336 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1338 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1339 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1340 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1341 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1343 DestroyWindow(hwndRichEdit);
1346 /* FIXME: need to test unimplemented options and robustly test wparam */
1347 static void test_EM_SETOPTIONS(void)
1349 HWND hwndRichEdit = new_richedit(NULL);
1350 static const char text[] = "Hello. My name is RichEdit!";
1351 char buffer[1024] = {0};
1353 /* NEGATIVE TESTING - NO OPTIONS SET */
1354 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1355 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1357 /* testing no readonly by sending 'a' to the control*/
1358 SetFocus(hwndRichEdit);
1359 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1360 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1362 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1363 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1365 /* READONLY - sending 'a' to the control */
1366 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1367 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1368 SetFocus(hwndRichEdit);
1369 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1370 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1371 ok(buffer[0]==text[0],
1372 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1374 DestroyWindow(hwndRichEdit);
1377 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1379 CHARFORMAT2W text_format;
1380 text_format.cbSize = sizeof(text_format);
1381 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1382 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1383 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1386 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1388 int link_present = 0;
1390 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1392 { /* control text is url; should get CFE_LINK */
1393 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1397 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1401 static HWND new_static_wnd(HWND parent) {
1402 return new_window("Static", 0, parent);
1405 static void test_EM_AUTOURLDETECT(void)
1407 /* DO NOT change the properties of the first two elements. To shorten the
1408 tests, all tests after WM_SETTEXT test just the first two elements -
1409 one non-URL and one URL */
1415 {"http://www.winehq.org", 1},
1416 {"http//winehq.org", 0},
1417 {"ww.winehq.org", 0},
1418 {"www.winehq.org", 1},
1419 {"ftp://192.168.1.1", 1},
1420 {"ftp//192.168.1.1", 0},
1421 {"mailto:your@email.com", 1},
1422 {"prospero:prosperoserver", 1},
1424 {"news:newserver", 1},
1425 {"wais:waisserver", 1}
1430 HWND hwndRichEdit, parent;
1432 /* All of the following should cause the URL to be detected */
1433 const char * templates_delim[] = {
1434 "This is some text with X on it",
1435 "This is some text with (X) on it",
1436 "This is some text with X\r on it",
1437 "This is some text with ---X--- on it",
1438 "This is some text with \"X\" on it",
1439 "This is some text with 'X' on it",
1440 "This is some text with 'X' on it",
1441 "This is some text with :X: on it",
1443 "This text ends with X",
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",
1451 "This is some text with (X on it",
1452 "This is some text with \rX on it",
1453 "This is some text with ---X on it",
1454 "This is some text with \"X on it",
1455 "This is some text with 'X on it",
1456 "This is some text with :X on it",
1458 /* None of these should cause the URL to be detected */
1459 const char * templates_non_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",
1468 "This is some text with |X on it",
1469 "This is some text with *X on it",
1470 "This is some text with /X on it",
1471 "This is some text with +X on it",
1472 "This is some text with %X on it",
1473 "This is some text with #X on it",
1474 "This is some text with @X on it",
1475 "This is some text with \\X on it",
1477 /* All of these cause the URL detection to be extended by one more byte,
1478 thus demonstrating that the tested character is considered as part
1480 const char * templates_xten_delim[] = {
1481 "This is some text with X| on it",
1482 "This is some text with X* on it",
1483 "This is some text with X/ on it",
1484 "This is some text with X+ on it",
1485 "This is some text with X% on it",
1486 "This is some text with X# on it",
1487 "This is some text with X@ on it",
1488 "This is some text with X\\ on it",
1492 parent = new_static_wnd(NULL);
1493 hwndRichEdit = new_richedit(parent);
1494 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1495 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1496 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1497 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1498 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1499 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1500 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1501 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1502 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1503 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1504 /* for each url, check the text to see if CFE_LINK effect is present */
1505 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1507 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1508 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1509 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1511 /* Link detection should happen immediately upon WM_SETTEXT */
1512 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1513 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1514 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1516 DestroyWindow(hwndRichEdit);
1518 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1519 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1520 hwndRichEdit = new_richedit(parent);
1522 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1527 at_pos = strchr(templates_delim[j], 'X');
1528 at_offset = at_pos - templates_delim[j];
1529 strncpy(buffer, templates_delim[j], at_offset);
1530 buffer[at_offset] = '\0';
1531 strcat(buffer, urls[i].text);
1532 strcat(buffer, templates_delim[j] + at_offset + 1);
1533 end_offset = at_offset + strlen(urls[i].text);
1535 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1536 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1538 /* This assumes no templates start with the URL itself, and that they
1539 have at least two characters before the URL text */
1540 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1541 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1542 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1543 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1544 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1545 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1549 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1550 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1551 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1552 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1556 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1557 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1558 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1559 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1561 if (buffer[end_offset] != '\0')
1563 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1564 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1565 if (buffer[end_offset +1] != '\0')
1567 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1568 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1573 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1578 at_pos = strchr(templates_non_delim[j], 'X');
1579 at_offset = at_pos - templates_non_delim[j];
1580 strncpy(buffer, templates_non_delim[j], at_offset);
1581 buffer[at_offset] = '\0';
1582 strcat(buffer, urls[i].text);
1583 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1584 end_offset = at_offset + strlen(urls[i].text);
1586 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1587 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1589 /* This assumes no templates start with the URL itself, and that they
1590 have at least two characters before the URL text */
1591 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1592 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1593 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1594 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1595 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1596 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1598 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1599 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1600 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1601 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1602 if (buffer[end_offset] != '\0')
1604 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1605 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1606 if (buffer[end_offset +1] != '\0')
1608 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1609 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1614 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1619 at_pos = strchr(templates_xten_delim[j], 'X');
1620 at_offset = at_pos - templates_xten_delim[j];
1621 strncpy(buffer, templates_xten_delim[j], at_offset);
1622 buffer[at_offset] = '\0';
1623 strcat(buffer, urls[i].text);
1624 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1625 end_offset = at_offset + strlen(urls[i].text);
1627 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1628 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1630 /* This assumes no templates start with the URL itself, and that they
1631 have at least two characters before the URL text */
1632 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1633 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1634 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1635 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1636 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1637 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1641 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1642 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1643 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1644 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1645 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1646 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1650 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1651 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1652 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1653 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1654 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1655 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1657 if (buffer[end_offset +1] != '\0')
1659 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1660 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1661 if (buffer[end_offset +2] != '\0')
1663 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1664 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1669 DestroyWindow(hwndRichEdit);
1670 hwndRichEdit = NULL;
1673 /* Test detection of URLs within normal text - WM_CHAR case. */
1674 /* Test only the first two URL examples for brevity */
1675 for (i = 0; i < 2; i++) {
1676 hwndRichEdit = new_richedit(parent);
1678 /* Also for brevity, test only the first three delimiters */
1679 for (j = 0; j < 3; j++) {
1685 at_pos = strchr(templates_delim[j], 'X');
1686 at_offset = at_pos - templates_delim[j];
1687 end_offset = at_offset + strlen(urls[i].text);
1689 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1690 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1691 for (u = 0; templates_delim[j][u]; u++) {
1692 if (templates_delim[j][u] == '\r') {
1693 simulate_typing_characters(hwndRichEdit, "\r");
1694 } else if (templates_delim[j][u] != 'X') {
1695 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1697 for (v = 0; urls[i].text[v]; v++) {
1698 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1702 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1704 /* This assumes no templates start with the URL itself, and that they
1705 have at least two characters before the URL text */
1706 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1707 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1708 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1710 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1711 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1715 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1716 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1717 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1718 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1722 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1723 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1724 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1725 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1727 if (buffer[end_offset] != '\0')
1729 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1730 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1731 if (buffer[end_offset +1] != '\0')
1733 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1734 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1738 /* The following will insert a paragraph break after the first character
1739 of the URL candidate, thus breaking the URL. It is expected that the
1740 CFE_LINK attribute should break across both pieces of the URL */
1741 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1742 simulate_typing_characters(hwndRichEdit, "\r");
1743 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1745 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1746 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1747 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1748 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1749 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1750 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1752 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1753 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1754 /* end_offset moved because of paragraph break */
1755 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1756 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1757 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1758 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1760 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1761 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1762 if (buffer[end_offset +2] != '\0')
1764 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1765 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1769 /* The following will remove the just-inserted paragraph break, thus
1770 restoring the URL */
1771 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1772 simulate_typing_characters(hwndRichEdit, "\b");
1773 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1775 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1776 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1777 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1778 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1779 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1780 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1784 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1785 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1786 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1787 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1791 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1792 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1793 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1794 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1796 if (buffer[end_offset] != '\0')
1798 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1799 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1800 if (buffer[end_offset +1] != '\0')
1802 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1803 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1807 DestroyWindow(hwndRichEdit);
1808 hwndRichEdit = NULL;
1811 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1812 /* Test just the first two URL examples for brevity */
1813 for (i = 0; i < 2; i++) {
1816 hwndRichEdit = new_richedit(parent);
1818 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1820 1) Set entire text, a la WM_SETTEXT
1821 2) Set a selection of the text to the URL
1822 3) Set a portion of the text at a time, which eventually results in
1824 All of them should give equivalent results
1827 /* Set entire text in one go, like WM_SETTEXT */
1828 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1833 st.codepage = CP_ACP;
1834 st.flags = ST_DEFAULT;
1836 at_pos = strchr(templates_delim[j], 'X');
1837 at_offset = at_pos - templates_delim[j];
1838 strncpy(buffer, templates_delim[j], at_offset);
1839 buffer[at_offset] = '\0';
1840 strcat(buffer, urls[i].text);
1841 strcat(buffer, templates_delim[j] + at_offset + 1);
1842 end_offset = at_offset + strlen(urls[i].text);
1844 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1845 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1847 /* This assumes no templates start with the URL itself, and that they
1848 have at least two characters before the URL text */
1849 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1850 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1851 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1852 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1853 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1854 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1858 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1859 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1860 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1861 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1865 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1866 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1867 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1868 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1870 if (buffer[end_offset] != '\0')
1872 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1873 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1874 if (buffer[end_offset +1] != '\0')
1876 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1877 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1882 /* Set selection with X to the URL */
1883 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1888 at_pos = strchr(templates_delim[j], 'X');
1889 at_offset = at_pos - templates_delim[j];
1890 end_offset = at_offset + strlen(urls[i].text);
1892 st.codepage = CP_ACP;
1893 st.flags = ST_DEFAULT;
1894 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1895 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1896 st.flags = ST_SELECTION;
1897 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1898 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1899 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1901 /* This assumes no templates start with the URL itself, and that they
1902 have at least two characters before the URL text */
1903 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1904 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1905 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1906 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1907 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1908 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1912 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1913 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1914 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1915 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1919 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1920 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1921 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1922 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1924 if (buffer[end_offset] != '\0')
1926 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1927 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1928 if (buffer[end_offset +1] != '\0')
1930 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1931 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1936 /* Set selection with X to the first character of the URL, then the rest */
1937 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1942 at_pos = strchr(templates_delim[j], 'X');
1943 at_offset = at_pos - templates_delim[j];
1944 end_offset = at_offset + strlen(urls[i].text);
1946 strcpy(buffer, "YY");
1947 buffer[0] = urls[i].text[0];
1949 st.codepage = CP_ACP;
1950 st.flags = ST_DEFAULT;
1951 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1952 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1953 st.flags = ST_SELECTION;
1954 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1955 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1956 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1957 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1958 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1960 /* This assumes no templates start with the URL itself, and that they
1961 have at least two characters before the URL text */
1962 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1963 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1964 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1965 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1966 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1967 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1971 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1972 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1973 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1974 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1978 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1979 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1980 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1981 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1983 if (buffer[end_offset] != '\0')
1985 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1986 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1987 if (buffer[end_offset +1] != '\0')
1989 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1990 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1995 DestroyWindow(hwndRichEdit);
1996 hwndRichEdit = NULL;
1999 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2000 /* Test just the first two URL examples for brevity */
2001 for (i = 0; i < 2; i++) {
2002 hwndRichEdit = new_richedit(parent);
2004 /* Set selection with X to the URL */
2005 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2010 at_pos = strchr(templates_delim[j], 'X');
2011 at_offset = at_pos - templates_delim[j];
2012 end_offset = at_offset + strlen(urls[i].text);
2014 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2015 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2016 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2017 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2018 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2020 /* This assumes no templates start with the URL itself, and that they
2021 have at least two characters before the URL text */
2022 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2023 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2024 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2025 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2026 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2027 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2031 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2032 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2033 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2034 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2038 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2039 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2040 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2041 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2043 if (buffer[end_offset] != '\0')
2045 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2046 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2047 if (buffer[end_offset +1] != '\0')
2049 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2050 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2055 /* Set selection with X to the first character of the URL, then the rest */
2056 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2061 at_pos = strchr(templates_delim[j], 'X');
2062 at_offset = at_pos - templates_delim[j];
2063 end_offset = at_offset + strlen(urls[i].text);
2065 strcpy(buffer, "YY");
2066 buffer[0] = urls[i].text[0];
2068 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2069 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2070 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2071 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2072 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2073 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2074 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2076 /* This assumes no templates start with the URL itself, and that they
2077 have at least two characters before the URL text */
2078 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2079 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2080 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2081 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2082 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2083 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2087 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2088 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2089 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2090 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2094 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2095 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2096 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2097 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2099 if (buffer[end_offset] != '\0')
2101 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2102 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2103 if (buffer[end_offset +1] != '\0')
2105 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2106 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2111 DestroyWindow(hwndRichEdit);
2112 hwndRichEdit = NULL;
2115 DestroyWindow(parent);
2118 static void test_EM_SCROLL(void)
2121 int r; /* return value */
2122 int expr; /* expected return value */
2123 HWND hwndRichEdit = new_richedit(NULL);
2124 int y_before, y_after; /* units of lines of text */
2126 /* test a richedit box containing a single line of text */
2127 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2129 for (i = 0; i < 4; i++) {
2130 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2132 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2133 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2134 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2135 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2136 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2137 "(i == %d)\n", y_after, i);
2141 * test a richedit box that will scroll. There are two general
2142 * cases: the case without any long lines and the case with a long
2145 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2147 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2149 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2150 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2151 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2152 "LONG LINE \nb\nc\nd\ne");
2153 for (j = 0; j < 12; j++) /* reset scroll position to top */
2154 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2156 /* get first visible line */
2157 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2158 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2160 /* get new current first visible line */
2161 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2163 ok(((r & 0xffffff00) == 0x00010000) &&
2164 ((r & 0x000000ff) != 0x00000000),
2165 "EM_SCROLL page down didn't scroll by a small positive number of "
2166 "lines (r == 0x%08x)\n", r);
2167 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2168 "(line %d scrolled to line %d\n", y_before, y_after);
2172 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2173 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2174 ok(((r & 0xffffff00) == 0x0001ff00),
2175 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2176 "(r == 0x%08x)\n", r);
2177 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2178 "%d scrolled to line %d\n", y_before, y_after);
2182 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2184 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2186 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2187 "(r == 0x%08x)\n", r);
2188 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2189 "1 line (%d scrolled to %d)\n", y_before, y_after);
2193 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2195 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2197 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2198 "(r == 0x%08x)\n", r);
2199 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2200 "line (%d scrolled to %d)\n", y_before, y_after);
2204 r = SendMessage(hwndRichEdit, EM_SCROLL,
2205 SB_LINEUP, 0); /* lineup beyond top */
2207 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2210 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2211 ok(y_before == y_after,
2212 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2216 r = SendMessage(hwndRichEdit, EM_SCROLL,
2217 SB_PAGEUP, 0);/*page up beyond top */
2219 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2222 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2223 ok(y_before == y_after,
2224 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2226 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2227 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2228 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2229 r = SendMessage(hwndRichEdit, EM_SCROLL,
2230 SB_PAGEDOWN, 0); /* page down beyond bot */
2231 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2234 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2235 ok(y_before == y_after,
2236 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2239 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2240 SendMessage(hwndRichEdit, EM_SCROLL,
2241 SB_LINEDOWN, 0); /* line down beyond bot */
2242 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2245 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2246 ok(y_before == y_after,
2247 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2250 DestroyWindow(hwndRichEdit);
2253 unsigned int recursionLevel = 0;
2254 unsigned int WM_SIZE_recursionLevel = 0;
2255 BOOL bailedOutOfRecursion = FALSE;
2256 LRESULT WINAPI (*richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2258 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2262 if (bailedOutOfRecursion) return 0;
2263 if (recursionLevel >= 32) {
2264 bailedOutOfRecursion = TRUE;
2271 WM_SIZE_recursionLevel++;
2272 r = richeditProc(hwnd, message, wParam, lParam);
2273 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2274 ShowScrollBar(hwnd, SB_VERT, TRUE);
2275 WM_SIZE_recursionLevel--;
2278 r = richeditProc(hwnd, message, wParam, lParam);
2285 static void test_scrollbar_visibility(void)
2288 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2293 /* These tests show that richedit should temporarily refrain from automatically
2294 hiding or showing its scrollbars (vertical at least) when an explicit request
2295 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2296 Some applications depend on forced showing (when otherwise richedit would
2297 hide the vertical scrollbar) and are thrown on an endless recursive loop
2298 if richedit auto-hides the scrollbar again. Apparently they never heard of
2299 the ES_DISABLENOSCROLL style... */
2301 hwndRichEdit = new_richedit(NULL);
2303 /* Test default scrollbar visibility behavior */
2304 memset(&si, 0, sizeof(si));
2305 si.cbSize = sizeof(si);
2306 si.fMask = SIF_PAGE | SIF_RANGE;
2307 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2308 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2309 "Vertical scrollbar is visible, should be invisible.\n");
2310 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2311 "reported page/range is %d (%d..%d) expected all 0\n",
2312 si.nPage, si.nMin, si.nMax);
2314 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2315 memset(&si, 0, sizeof(si));
2316 si.cbSize = sizeof(si);
2317 si.fMask = SIF_PAGE | SIF_RANGE;
2318 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2319 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2320 "Vertical scrollbar is visible, should be invisible.\n");
2321 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2322 "reported page/range is %d (%d..%d) expected all 0\n",
2323 si.nPage, si.nMin, si.nMax);
2325 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2326 memset(&si, 0, sizeof(si));
2327 si.cbSize = sizeof(si);
2328 si.fMask = SIF_PAGE | SIF_RANGE;
2329 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2330 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2331 "Vertical scrollbar is invisible, should be visible.\n");
2332 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2333 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2334 si.nPage, si.nMin, si.nMax);
2336 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2337 even though it hides the scrollbar */
2338 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2339 memset(&si, 0, sizeof(si));
2340 si.cbSize = sizeof(si);
2341 si.fMask = SIF_PAGE | SIF_RANGE;
2342 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2343 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2344 "Vertical scrollbar is visible, should be invisible.\n");
2345 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2346 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2347 si.nPage, si.nMin, si.nMax);
2349 /* Setting non-scrolling text again does *not* reset scrollbar range */
2350 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2351 memset(&si, 0, sizeof(si));
2352 si.cbSize = sizeof(si);
2353 si.fMask = SIF_PAGE | SIF_RANGE;
2354 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2355 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2356 "Vertical scrollbar is visible, should be invisible.\n");
2357 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2358 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2359 si.nPage, si.nMin, si.nMax);
2361 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2362 memset(&si, 0, sizeof(si));
2363 si.cbSize = sizeof(si);
2364 si.fMask = SIF_PAGE | SIF_RANGE;
2365 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2366 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2367 "Vertical scrollbar is visible, should be invisible.\n");
2368 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2369 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2370 si.nPage, si.nMin, si.nMax);
2372 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2373 memset(&si, 0, sizeof(si));
2374 si.cbSize = sizeof(si);
2375 si.fMask = SIF_PAGE | SIF_RANGE;
2376 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2377 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2378 "Vertical scrollbar is visible, should be invisible.\n");
2379 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2380 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2381 si.nPage, si.nMin, si.nMax);
2383 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2384 memset(&si, 0, sizeof(si));
2385 si.cbSize = sizeof(si);
2386 si.fMask = SIF_PAGE | SIF_RANGE;
2387 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2388 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2389 "Vertical scrollbar is visible, should be invisible.\n");
2390 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2391 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2392 si.nPage, si.nMin, si.nMax);
2394 DestroyWindow(hwndRichEdit);
2396 /* Test again, with ES_DISABLENOSCROLL style */
2397 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2399 /* Test default scrollbar visibility behavior */
2400 memset(&si, 0, sizeof(si));
2401 si.cbSize = sizeof(si);
2402 si.fMask = SIF_PAGE | SIF_RANGE;
2403 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2404 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2405 "Vertical scrollbar is invisible, should be visible.\n");
2406 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2407 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2408 si.nPage, si.nMin, si.nMax);
2410 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2411 memset(&si, 0, sizeof(si));
2412 si.cbSize = sizeof(si);
2413 si.fMask = SIF_PAGE | SIF_RANGE;
2414 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2415 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2416 "Vertical scrollbar is invisible, should be visible.\n");
2417 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2418 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2419 si.nPage, si.nMin, si.nMax);
2421 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2422 memset(&si, 0, sizeof(si));
2423 si.cbSize = sizeof(si);
2424 si.fMask = SIF_PAGE | SIF_RANGE;
2425 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2426 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2427 "Vertical scrollbar is invisible, should be visible.\n");
2428 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2429 "reported page/range is %d (%d..%d)\n",
2430 si.nPage, si.nMin, si.nMax);
2432 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2433 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2434 memset(&si, 0, sizeof(si));
2435 si.cbSize = sizeof(si);
2436 si.fMask = SIF_PAGE | SIF_RANGE;
2437 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2438 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2439 "Vertical scrollbar is invisible, should be visible.\n");
2440 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2441 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2442 si.nPage, si.nMin, si.nMax);
2444 /* Setting non-scrolling text again does *not* reset scrollbar range */
2445 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2446 memset(&si, 0, sizeof(si));
2447 si.cbSize = sizeof(si);
2448 si.fMask = SIF_PAGE | SIF_RANGE;
2449 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2450 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2451 "Vertical scrollbar is invisible, should be visible.\n");
2452 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2453 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2454 si.nPage, si.nMin, si.nMax);
2456 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2457 memset(&si, 0, sizeof(si));
2458 si.cbSize = sizeof(si);
2459 si.fMask = SIF_PAGE | SIF_RANGE;
2460 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2461 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2462 "Vertical scrollbar is invisible, should be visible.\n");
2463 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2464 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2465 si.nPage, si.nMin, si.nMax);
2467 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2468 memset(&si, 0, sizeof(si));
2469 si.cbSize = sizeof(si);
2470 si.fMask = SIF_PAGE | SIF_RANGE;
2471 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2472 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2473 "Vertical scrollbar is invisible, should be visible.\n");
2474 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2475 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2476 si.nPage, si.nMin, si.nMax);
2478 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2479 memset(&si, 0, sizeof(si));
2480 si.cbSize = sizeof(si);
2481 si.fMask = SIF_PAGE | SIF_RANGE;
2482 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2483 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2484 "Vertical scrollbar is invisible, should be visible.\n");
2485 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2486 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2487 si.nPage, si.nMin, si.nMax);
2489 DestroyWindow(hwndRichEdit);
2491 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2492 hwndRichEdit = new_richedit(NULL);
2494 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2495 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2496 memset(&si, 0, sizeof(si));
2497 si.cbSize = sizeof(si);
2498 si.fMask = SIF_PAGE | SIF_RANGE;
2499 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2500 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2501 "Vertical scrollbar is invisible, should be visible.\n");
2503 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2504 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2505 si.nPage, si.nMin, si.nMax);
2508 /* Ditto, see above */
2509 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2510 memset(&si, 0, sizeof(si));
2511 si.cbSize = sizeof(si);
2512 si.fMask = SIF_PAGE | SIF_RANGE;
2513 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2514 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2515 "Vertical scrollbar is invisible, should be visible.\n");
2517 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2518 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2519 si.nPage, si.nMin, si.nMax);
2522 /* Ditto, see above */
2523 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2524 memset(&si, 0, sizeof(si));
2525 si.cbSize = sizeof(si);
2526 si.fMask = SIF_PAGE | SIF_RANGE;
2527 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2528 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2529 "Vertical scrollbar is invisible, should be visible.\n");
2531 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2532 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2533 si.nPage, si.nMin, si.nMax);
2536 /* Ditto, see above */
2537 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2538 memset(&si, 0, sizeof(si));
2539 si.cbSize = sizeof(si);
2540 si.fMask = SIF_PAGE | SIF_RANGE;
2541 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2542 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2543 "Vertical scrollbar is invisible, should be visible.\n");
2545 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2546 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2547 si.nPage, si.nMin, si.nMax);
2550 /* Ditto, see above */
2551 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2552 memset(&si, 0, sizeof(si));
2553 si.cbSize = sizeof(si);
2554 si.fMask = SIF_PAGE | SIF_RANGE;
2555 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2556 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2557 "Vertical scrollbar is invisible, should be visible.\n");
2559 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2560 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2561 si.nPage, si.nMin, si.nMax);
2564 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2565 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2566 memset(&si, 0, sizeof(si));
2567 si.cbSize = sizeof(si);
2568 si.fMask = SIF_PAGE | SIF_RANGE;
2569 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2570 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2571 "Vertical scrollbar is visible, should be invisible.\n");
2572 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2573 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2574 si.nPage, si.nMin, si.nMax);
2576 DestroyWindow(hwndRichEdit);
2578 hwndRichEdit = new_richedit(NULL);
2580 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2581 memset(&si, 0, sizeof(si));
2582 si.cbSize = sizeof(si);
2583 si.fMask = SIF_PAGE | SIF_RANGE;
2584 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2585 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2586 "Vertical scrollbar is visible, should be invisible.\n");
2587 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2588 "reported page/range is %d (%d..%d) expected all 0\n",
2589 si.nPage, si.nMin, si.nMax);
2591 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2592 memset(&si, 0, sizeof(si));
2593 si.cbSize = sizeof(si);
2594 si.fMask = SIF_PAGE | SIF_RANGE;
2595 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2596 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2597 "Vertical scrollbar is visible, should be invisible.\n");
2598 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2599 "reported page/range is %d (%d..%d) expected all 0\n",
2600 si.nPage, si.nMin, si.nMax);
2602 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2603 memset(&si, 0, sizeof(si));
2604 si.cbSize = sizeof(si);
2605 si.fMask = SIF_PAGE | SIF_RANGE;
2606 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2607 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2608 "Vertical scrollbar is visible, should be invisible.\n");
2609 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2610 "reported page/range is %d (%d..%d) expected all 0\n",
2611 si.nPage, si.nMin, si.nMax);
2613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2614 memset(&si, 0, sizeof(si));
2615 si.cbSize = sizeof(si);
2616 si.fMask = SIF_PAGE | SIF_RANGE;
2617 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2618 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2619 "Vertical scrollbar is visible, should be invisible.\n");
2620 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2621 "reported page/range is %d (%d..%d) expected all 0\n",
2622 si.nPage, si.nMin, si.nMax);
2624 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2625 memset(&si, 0, sizeof(si));
2626 si.cbSize = sizeof(si);
2627 si.fMask = SIF_PAGE | SIF_RANGE;
2628 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2629 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2630 "Vertical scrollbar is invisible, should be visible.\n");
2631 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2632 "reported page/range is %d (%d..%d)\n",
2633 si.nPage, si.nMin, si.nMax);
2635 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2636 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2637 memset(&si, 0, sizeof(si));
2638 si.cbSize = sizeof(si);
2639 si.fMask = SIF_PAGE | SIF_RANGE;
2640 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2641 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2642 "Vertical scrollbar is visible, should be invisible.\n");
2643 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2644 "reported page/range is %d (%d..%d)\n",
2645 si.nPage, si.nMin, si.nMax);
2647 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2648 memset(&si, 0, sizeof(si));
2649 si.cbSize = sizeof(si);
2650 si.fMask = SIF_PAGE | SIF_RANGE;
2651 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2652 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2653 "Vertical scrollbar is visible, should be invisible.\n");
2654 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2655 "reported page/range is %d (%d..%d)\n",
2656 si.nPage, si.nMin, si.nMax);
2658 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2659 EM_SCROLL will make visible any forcefully invisible scrollbar */
2660 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2661 memset(&si, 0, sizeof(si));
2662 si.cbSize = sizeof(si);
2663 si.fMask = SIF_PAGE | SIF_RANGE;
2664 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2665 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2666 "Vertical scrollbar is invisible, should be visible.\n");
2667 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2668 "reported page/range is %d (%d..%d)\n",
2669 si.nPage, si.nMin, si.nMax);
2671 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2672 memset(&si, 0, sizeof(si));
2673 si.cbSize = sizeof(si);
2674 si.fMask = SIF_PAGE | SIF_RANGE;
2675 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2676 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2677 "Vertical scrollbar is visible, should be invisible.\n");
2678 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2679 "reported page/range is %d (%d..%d)\n",
2680 si.nPage, si.nMin, si.nMax);
2682 /* Again, EM_SCROLL, with SB_LINEUP */
2683 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2684 memset(&si, 0, sizeof(si));
2685 si.cbSize = sizeof(si);
2686 si.fMask = SIF_PAGE | SIF_RANGE;
2687 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2688 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2689 "Vertical scrollbar is invisible, should be visible.\n");
2690 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2691 "reported page/range is %d (%d..%d)\n",
2692 si.nPage, si.nMin, si.nMax);
2694 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2695 memset(&si, 0, sizeof(si));
2696 si.cbSize = sizeof(si);
2697 si.fMask = SIF_PAGE | SIF_RANGE;
2698 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2699 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2700 "Vertical scrollbar is visible, should be invisible.\n");
2701 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2702 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2703 si.nPage, si.nMin, si.nMax);
2705 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2706 memset(&si, 0, sizeof(si));
2707 si.cbSize = sizeof(si);
2708 si.fMask = SIF_PAGE | SIF_RANGE;
2709 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2710 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2711 "Vertical scrollbar is invisible, should be visible.\n");
2712 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2713 "reported page/range is %d (%d..%d)\n",
2714 si.nPage, si.nMin, si.nMax);
2716 DestroyWindow(hwndRichEdit);
2719 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2720 hwndRichEdit = new_richedit(NULL);
2722 #define ENABLE_WS_VSCROLL(hwnd) \
2723 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2724 #define DISABLE_WS_VSCROLL(hwnd) \
2725 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2727 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2728 ENABLE_WS_VSCROLL(hwndRichEdit);
2729 memset(&si, 0, sizeof(si));
2730 si.cbSize = sizeof(si);
2731 si.fMask = SIF_PAGE | SIF_RANGE;
2732 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2733 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2734 "Vertical scrollbar is invisible, should be visible.\n");
2735 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2736 "reported page/range is %d (%d..%d) expected all 0\n",
2737 si.nPage, si.nMin, si.nMax);
2739 /* Ditto, see above */
2740 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2741 memset(&si, 0, sizeof(si));
2742 si.cbSize = sizeof(si);
2743 si.fMask = SIF_PAGE | SIF_RANGE;
2744 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2745 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2746 "Vertical scrollbar is invisible, should be visible.\n");
2747 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2748 "reported page/range is %d (%d..%d) expected all 0\n",
2749 si.nPage, si.nMin, si.nMax);
2751 /* Ditto, see above */
2752 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2753 memset(&si, 0, sizeof(si));
2754 si.cbSize = sizeof(si);
2755 si.fMask = SIF_PAGE | SIF_RANGE;
2756 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2757 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2758 "Vertical scrollbar is invisible, should be visible.\n");
2759 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2760 "reported page/range is %d (%d..%d) expected all 0\n",
2761 si.nPage, si.nMin, si.nMax);
2763 /* Ditto, see above */
2764 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2765 memset(&si, 0, sizeof(si));
2766 si.cbSize = sizeof(si);
2767 si.fMask = SIF_PAGE | SIF_RANGE;
2768 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2769 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2770 "Vertical scrollbar is invisible, should be visible.\n");
2771 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2772 "reported page/range is %d (%d..%d) expected all 0\n",
2773 si.nPage, si.nMin, si.nMax);
2775 /* Ditto, see above */
2776 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2777 memset(&si, 0, sizeof(si));
2778 si.cbSize = sizeof(si);
2779 si.fMask = SIF_PAGE | SIF_RANGE;
2780 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2781 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2782 "Vertical scrollbar is invisible, should be visible.\n");
2783 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2784 "reported page/range is %d (%d..%d) expected all 0\n",
2785 si.nPage, si.nMin, si.nMax);
2787 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2788 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2789 memset(&si, 0, sizeof(si));
2790 si.cbSize = sizeof(si);
2791 si.fMask = SIF_PAGE | SIF_RANGE;
2792 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2793 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2794 "Vertical scrollbar is visible, should be invisible.\n");
2795 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2796 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2797 si.nPage, si.nMin, si.nMax);
2799 DestroyWindow(hwndRichEdit);
2801 hwndRichEdit = new_richedit(NULL);
2803 DISABLE_WS_VSCROLL(hwndRichEdit);
2804 memset(&si, 0, sizeof(si));
2805 si.cbSize = sizeof(si);
2806 si.fMask = SIF_PAGE | SIF_RANGE;
2807 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2808 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2809 "Vertical scrollbar is visible, should be invisible.\n");
2810 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2811 "reported page/range is %d (%d..%d) expected all 0\n",
2812 si.nPage, si.nMin, si.nMax);
2814 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2815 memset(&si, 0, sizeof(si));
2816 si.cbSize = sizeof(si);
2817 si.fMask = SIF_PAGE | SIF_RANGE;
2818 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2819 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2820 "Vertical scrollbar is visible, should be invisible.\n");
2821 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2822 "reported page/range is %d (%d..%d) expected all 0\n",
2823 si.nPage, si.nMin, si.nMax);
2825 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2826 memset(&si, 0, sizeof(si));
2827 si.cbSize = sizeof(si);
2828 si.fMask = SIF_PAGE | SIF_RANGE;
2829 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2830 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2831 "Vertical scrollbar is visible, should be invisible.\n");
2832 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2833 "reported page/range is %d (%d..%d) expected all 0\n",
2834 si.nPage, si.nMin, si.nMax);
2836 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2837 memset(&si, 0, sizeof(si));
2838 si.cbSize = sizeof(si);
2839 si.fMask = SIF_PAGE | SIF_RANGE;
2840 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2841 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2842 "Vertical scrollbar is visible, should be invisible.\n");
2843 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0,
2844 "reported page/range is %d (%d..%d) expected all 0\n",
2845 si.nPage, si.nMin, si.nMax);
2847 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2848 memset(&si, 0, sizeof(si));
2849 si.cbSize = sizeof(si);
2850 si.fMask = SIF_PAGE | SIF_RANGE;
2851 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2852 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2853 "Vertical scrollbar is invisible, should be visible.\n");
2854 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2855 "reported page/range is %d (%d..%d)\n",
2856 si.nPage, si.nMin, si.nMax);
2858 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2859 DISABLE_WS_VSCROLL(hwndRichEdit);
2860 memset(&si, 0, sizeof(si));
2861 si.cbSize = sizeof(si);
2862 si.fMask = SIF_PAGE | SIF_RANGE;
2863 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2864 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2865 "Vertical scrollbar is visible, should be invisible.\n");
2866 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2867 "reported page/range is %d (%d..%d)\n",
2868 si.nPage, si.nMin, si.nMax);
2870 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2871 memset(&si, 0, sizeof(si));
2872 si.cbSize = sizeof(si);
2873 si.fMask = SIF_PAGE | SIF_RANGE;
2874 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2875 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2876 "Vertical scrollbar is visible, should be invisible.\n");
2877 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2878 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2879 si.nPage, si.nMin, si.nMax);
2881 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2882 memset(&si, 0, sizeof(si));
2883 si.cbSize = sizeof(si);
2884 si.fMask = SIF_PAGE | SIF_RANGE;
2885 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2886 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2887 "Vertical scrollbar is invisible, should be visible.\n");
2888 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2889 "reported page/range is %d (%d..%d)\n",
2890 si.nPage, si.nMin, si.nMax);
2892 DISABLE_WS_VSCROLL(hwndRichEdit);
2893 memset(&si, 0, sizeof(si));
2894 si.cbSize = sizeof(si);
2895 si.fMask = SIF_PAGE | SIF_RANGE;
2896 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2897 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2898 "Vertical scrollbar is visible, should be invisible.\n");
2899 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2900 "reported page/range is %d (%d..%d)\n",
2901 si.nPage, si.nMin, si.nMax);
2903 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2904 EM_SCROLL will make visible any forcefully invisible scrollbar */
2905 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2906 memset(&si, 0, sizeof(si));
2907 si.cbSize = sizeof(si);
2908 si.fMask = SIF_PAGE | SIF_RANGE;
2909 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2910 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2911 "Vertical scrollbar is invisible, should be visible.\n");
2912 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2913 "reported page/range is %d (%d..%d)\n",
2914 si.nPage, si.nMin, si.nMax);
2916 DISABLE_WS_VSCROLL(hwndRichEdit);
2917 memset(&si, 0, sizeof(si));
2918 si.cbSize = sizeof(si);
2919 si.fMask = SIF_PAGE | SIF_RANGE;
2920 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2921 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2922 "Vertical scrollbar is visible, should be invisible.\n");
2923 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2924 "reported page/range is %d (%d..%d)\n",
2925 si.nPage, si.nMin, si.nMax);
2927 /* Again, EM_SCROLL, with SB_LINEUP */
2928 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2929 memset(&si, 0, sizeof(si));
2930 si.cbSize = sizeof(si);
2931 si.fMask = SIF_PAGE | SIF_RANGE;
2932 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2933 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2934 "Vertical scrollbar is invisible, should be visible.\n");
2935 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2936 "reported page/range is %d (%d..%d)\n",
2937 si.nPage, si.nMin, si.nMax);
2939 DestroyWindow(hwndRichEdit);
2941 /* This window proc models what is going on with Corman Lisp 3.0.
2942 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
2943 force the scrollbar into visibility. Recursion should NOT happen
2944 as a result of this action.
2946 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
2948 richeditProc = cls.lpfnWndProc;
2949 cls.lpfnWndProc = RicheditStupidOverrideProcA;
2950 cls.lpszClassName = "RicheditStupidOverride";
2951 if(!RegisterClassA(&cls)) assert(0);
2954 WM_SIZE_recursionLevel = 0;
2955 bailedOutOfRecursion = FALSE;
2956 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
2957 ok(!bailedOutOfRecursion,
2958 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2961 WM_SIZE_recursionLevel = 0;
2962 bailedOutOfRecursion = FALSE;
2963 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
2964 ok(!bailedOutOfRecursion,
2965 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
2967 /* Unblock window in order to process WM_DESTROY */
2969 bailedOutOfRecursion = FALSE;
2970 WM_SIZE_recursionLevel = 0;
2971 DestroyWindow(hwndRichEdit);
2975 static void test_EM_SETUNDOLIMIT(void)
2977 /* cases we test for:
2978 * default behaviour - limiting at 100 undo's
2979 * undo disabled - setting a limit of 0
2980 * undo limited - undo limit set to some to some number, like 2
2981 * bad input - sending a negative number should default to 100 undo's */
2983 HWND hwndRichEdit = new_richedit(NULL);
2988 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2991 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2992 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2993 also, multiple pastes don't combine like WM_CHAR would */
2994 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2996 /* first case - check the default */
2997 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2998 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2999 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3000 for (i=0; i<100; i++) /* Undo 100 of them */
3001 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3002 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3003 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3005 /* second case - cannot undo */
3006 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3007 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3008 SendMessage(hwndRichEdit,
3009 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3010 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3011 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3013 /* third case - set it to an arbitrary number */
3014 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3015 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3016 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3017 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3018 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3019 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3020 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3021 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3022 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3023 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3024 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3025 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3026 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3027 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3029 /* fourth case - setting negative numbers should default to 100 undos */
3030 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3031 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3033 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3035 DestroyWindow(hwndRichEdit);
3038 static void test_ES_PASSWORD(void)
3040 /* This isn't hugely testable, so we're just going to run it through its paces */
3042 HWND hwndRichEdit = new_richedit(NULL);
3045 /* First, check the default of a regular control */
3046 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3048 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3050 /* Now, set it to something normal */
3051 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3052 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3054 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3056 /* Now, set it to something odd */
3057 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3058 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3060 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3061 DestroyWindow(hwndRichEdit);
3064 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3069 char** str = (char**)dwCookie;
3072 memcpy(*str, pbBuff, *pcb);
3078 static void test_WM_SETTEXT()
3080 HWND hwndRichEdit = new_richedit(NULL);
3081 const char * TestItem1 = "TestSomeText";
3082 const char * TestItem2 = "TestSomeText\r";
3083 const char * TestItem2_after = "TestSomeText\r\n";
3084 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3085 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3086 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3087 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3088 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3089 const char * TestItem5_after = "TestSomeText TestSomeText";
3090 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3091 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3092 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3093 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3095 char buf[1024] = {0};
3100 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3101 any solitary \r to be converted to \r\n on return. Properly paired
3102 \r\n are not affected. It also shows that the special sequence \r\r\n
3103 gets converted to a single space.
3106 #define TEST_SETTEXT(a, b) \
3107 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3108 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3109 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3110 ok (result == lstrlen(buf), \
3111 "WM_GETTEXT returned %ld instead of expected %u\n", \
3112 result, lstrlen(buf)); \
3113 result = strcmp(b, buf); \
3115 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3117 TEST_SETTEXT(TestItem1, TestItem1)
3118 TEST_SETTEXT(TestItem2, TestItem2_after)
3119 TEST_SETTEXT(TestItem3, TestItem3_after)
3120 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3121 TEST_SETTEXT(TestItem4, TestItem4_after)
3122 TEST_SETTEXT(TestItem5, TestItem5_after)
3123 TEST_SETTEXT(TestItem6, TestItem6_after)
3124 TEST_SETTEXT(TestItem7, TestItem7_after)
3126 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3127 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3129 es.dwCookie = (DWORD_PTR)&p;
3131 es.pfnCallback = test_WM_SETTEXT_esCallback;
3132 memset(buf, 0, sizeof(buf));
3133 SendMessage(hwndRichEdit, EM_STREAMOUT,
3134 (WPARAM)(SF_RTF), (LPARAM)&es);
3135 trace("EM_STREAMOUT produced: \n%s\n", buf);
3136 TEST_SETTEXT(buf, TestItem1)
3139 DestroyWindow(hwndRichEdit);
3142 static void test_EM_STREAMOUT(void)
3144 HWND hwndRichEdit = new_richedit(NULL);
3147 char buf[1024] = {0};
3150 const char * TestItem1 = "TestSomeText";
3151 const char * TestItem2 = "TestSomeText\r";
3152 const char * TestItem3 = "TestSomeText\r\n";
3154 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3156 es.dwCookie = (DWORD_PTR)&p;
3158 es.pfnCallback = test_WM_SETTEXT_esCallback;
3159 memset(buf, 0, sizeof(buf));
3160 SendMessage(hwndRichEdit, EM_STREAMOUT,
3161 (WPARAM)(SF_TEXT), (LPARAM)&es);
3163 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3164 ok(strcmp(buf, TestItem1) == 0,
3165 "streamed text different, got %s\n", buf);
3167 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3169 es.dwCookie = (DWORD_PTR)&p;
3171 es.pfnCallback = test_WM_SETTEXT_esCallback;
3172 memset(buf, 0, sizeof(buf));
3173 SendMessage(hwndRichEdit, EM_STREAMOUT,
3174 (WPARAM)(SF_TEXT), (LPARAM)&es);
3176 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3177 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3178 ok(strcmp(buf, TestItem3) == 0,
3179 "streamed text different from, got %s\n", buf);
3180 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3182 es.dwCookie = (DWORD_PTR)&p;
3184 es.pfnCallback = test_WM_SETTEXT_esCallback;
3185 memset(buf, 0, sizeof(buf));
3186 SendMessage(hwndRichEdit, EM_STREAMOUT,
3187 (WPARAM)(SF_TEXT), (LPARAM)&es);
3189 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3190 ok(strcmp(buf, TestItem3) == 0,
3191 "streamed text different, got %s\n", buf);
3193 DestroyWindow(hwndRichEdit);
3196 static void test_EM_SETTEXTEX(void)
3198 HWND hwndRichEdit = new_richedit(NULL);
3201 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3203 'T', 'e', 'x', 't', 0};
3204 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3210 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3214 const char * TestItem2_after = "TestSomeText\r\n";
3215 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3218 '\r','\n','\r','\n', 0};
3219 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3223 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3227 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3230 '\r','\r','\n','\r',
3232 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3236 #define MAX_BUF_LEN 1024
3237 WCHAR buf[MAX_BUF_LEN];
3238 char bufACP[MAX_BUF_LEN];
3244 setText.codepage = 1200; /* no constant for unicode */
3245 getText.codepage = 1200; /* no constant for unicode */
3246 getText.cb = MAX_BUF_LEN;
3247 getText.flags = GT_DEFAULT;
3248 getText.lpDefaultChar = NULL;
3249 getText.lpUsedDefChar = NULL;
3252 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3253 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3254 ok(lstrcmpW(buf, TestItem1) == 0,
3255 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3257 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3258 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3260 setText.codepage = 1200; /* no constant for unicode */
3261 getText.codepage = 1200; /* no constant for unicode */
3262 getText.cb = MAX_BUF_LEN;
3263 getText.flags = GT_DEFAULT;
3264 getText.lpDefaultChar = NULL;
3265 getText.lpUsedDefChar = NULL;
3267 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3268 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3269 ok(lstrcmpW(buf, TestItem2) == 0,
3270 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3272 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3273 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3274 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3275 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3277 /* Baseline test for just-enough buffer space for string */
3278 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3279 getText.codepage = 1200; /* no constant for unicode */
3280 getText.flags = GT_DEFAULT;
3281 getText.lpDefaultChar = NULL;
3282 getText.lpUsedDefChar = NULL;
3283 memset(buf, 0, MAX_BUF_LEN);
3284 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3285 ok(lstrcmpW(buf, TestItem2) == 0,
3286 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3288 /* When there is enough space for one character, but not both, of the CRLF
3289 pair at the end of the string, the CR is not copied at all. That is,
3290 the caller must not see CRLF pairs truncated to CR at the end of the
3293 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3294 getText.codepage = 1200; /* no constant for unicode */
3295 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3296 getText.lpDefaultChar = NULL;
3297 getText.lpUsedDefChar = NULL;
3298 memset(buf, 0, MAX_BUF_LEN);
3299 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3300 ok(lstrcmpW(buf, TestItem1) == 0,
3301 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3304 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3305 setText.codepage = 1200; /* no constant for unicode */
3306 getText.codepage = 1200; /* no constant for unicode */
3307 getText.cb = MAX_BUF_LEN;
3308 getText.flags = GT_DEFAULT;
3309 getText.lpDefaultChar = NULL;
3310 getText.lpUsedDefChar = NULL;
3312 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3313 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3314 ok(lstrcmpW(buf, TestItem3_after) == 0,
3315 "EM_SETTEXTEX did not convert properly\n");
3317 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3318 setText.codepage = 1200; /* no constant for unicode */
3319 getText.codepage = 1200; /* no constant for unicode */
3320 getText.cb = MAX_BUF_LEN;
3321 getText.flags = GT_DEFAULT;
3322 getText.lpDefaultChar = NULL;
3323 getText.lpUsedDefChar = NULL;
3325 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3326 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3327 ok(lstrcmpW(buf, TestItem3_after) == 0,
3328 "EM_SETTEXTEX did not convert properly\n");
3330 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3331 setText.codepage = 1200; /* no constant for unicode */
3332 getText.codepage = 1200; /* no constant for unicode */
3333 getText.cb = MAX_BUF_LEN;
3334 getText.flags = GT_DEFAULT;
3335 getText.lpDefaultChar = NULL;
3336 getText.lpUsedDefChar = NULL;
3338 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3339 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3340 ok(lstrcmpW(buf, TestItem4_after) == 0,
3341 "EM_SETTEXTEX did not convert properly\n");
3343 /* !ST_SELECTION && Unicode && !\rtf */
3344 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3345 (WPARAM)&setText, (LPARAM) NULL);
3346 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3349 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3350 ok(lstrlenW(buf) == 0,
3351 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3353 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3355 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3356 /* select some text */
3359 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3360 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3361 setText.flags = ST_SELECTION;
3362 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3363 (WPARAM)&setText, (LPARAM) NULL);
3365 "EM_SETTEXTEX with NULL lParam to replace selection"
3366 " with no text should return 0. Got %i\n",
3369 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3371 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3372 /* select some text */
3375 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3376 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3377 setText.flags = ST_SELECTION;
3378 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3379 (WPARAM)&setText, (LPARAM) TestItem1);
3381 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3382 ok(result == lstrlenW(TestItem1),
3383 "EM_SETTEXTEX with NULL lParam to replace selection"
3384 " with no text should return 0. Got %i\n",
3386 ok(lstrlenW(buf) == 22,
3387 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3390 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3391 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3393 es.dwCookie = (DWORD_PTR)&p;
3395 es.pfnCallback = test_WM_SETTEXT_esCallback;
3396 memset(buf, 0, sizeof(buf));
3397 SendMessage(hwndRichEdit, EM_STREAMOUT,
3398 (WPARAM)(SF_RTF), (LPARAM)&es);
3399 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3401 /* !ST_SELECTION && !Unicode && \rtf */
3402 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3403 getText.codepage = 1200; /* no constant for unicode */
3404 getText.cb = MAX_BUF_LEN;
3405 getText.flags = GT_DEFAULT;
3406 getText.lpDefaultChar = NULL;
3407 getText.lpUsedDefChar = NULL;
3410 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3411 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3412 ok(lstrcmpW(buf, TestItem1) == 0,
3413 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3415 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3416 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3418 es.dwCookie = (DWORD_PTR)&p;
3420 es.pfnCallback = test_WM_SETTEXT_esCallback;
3421 memset(buf, 0, sizeof(buf));
3422 SendMessage(hwndRichEdit, EM_STREAMOUT,
3423 (WPARAM)(SF_RTF), (LPARAM)&es);
3424 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3426 /* select some text */
3429 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3431 /* ST_SELECTION && !Unicode && \rtf */
3432 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3433 getText.codepage = 1200; /* no constant for unicode */
3434 getText.cb = MAX_BUF_LEN;
3435 getText.flags = GT_DEFAULT;
3436 getText.lpDefaultChar = NULL;
3437 getText.lpUsedDefChar = NULL;
3439 setText.flags = ST_SELECTION;
3440 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3441 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3442 ok(lstrcmpW(buf, TestItem1alt) == 0,
3443 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3444 " using ST_SELECTION on an RTF string and non-Unicode\n");
3446 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3447 setText.codepage = 1200; /* no constant for unicode */
3448 getText.codepage = CP_ACP;
3449 getText.cb = MAX_BUF_LEN;
3452 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3453 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3455 /* select some text */
3458 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3460 /* ST_SELECTION && !Unicode && !\rtf */
3461 setText.codepage = CP_ACP;
3462 getText.codepage = 1200; /* no constant for unicode */
3463 getText.cb = MAX_BUF_LEN;
3464 getText.flags = GT_DEFAULT;
3465 getText.lpDefaultChar = NULL;
3466 getText.lpUsedDefChar = NULL;
3468 setText.flags = ST_SELECTION;
3469 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3470 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3471 ok(lstrcmpW(buf, TestItem1alt) == 0,
3472 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3473 " using ST_SELECTION and non-Unicode\n");
3476 DestroyWindow(hwndRichEdit);
3479 static void test_EM_LIMITTEXT(void)
3483 HWND hwndRichEdit = new_richedit(NULL);
3485 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3486 * about setting the length to -1 for multiline edit controls doesn't happen.
3489 /* Don't check default gettextlimit case. That's done in other tests */
3491 /* Set textlimit to 100 */
3492 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3493 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3495 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3497 /* Set textlimit to 0 */
3498 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3499 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3501 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3503 /* Set textlimit to -1 */
3504 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3505 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3507 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3509 /* Set textlimit to -2 */
3510 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3511 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3513 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3515 DestroyWindow (hwndRichEdit);
3519 static void test_EM_EXLIMITTEXT(void)
3521 int i, selBegin, selEnd, len1, len2;
3523 char text[1024 + 1];
3524 char buffer[1024 + 1];
3525 int textlimit = 0; /* multiple of 100 */
3526 HWND hwndRichEdit = new_richedit(NULL);
3528 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3529 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3532 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3533 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3535 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3538 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3539 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3541 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3543 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3544 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3545 /* default for WParam = 0 */
3546 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3548 textlimit = sizeof(text)-1;
3549 memset(text, 'W', textlimit);
3550 text[sizeof(text)-1] = 0;
3551 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3552 /* maxed out text */
3553 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3555 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3556 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3557 len1 = selEnd - selBegin;
3559 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3560 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3561 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3562 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3563 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3564 len2 = selEnd - selBegin;
3567 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3570 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3571 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3572 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3573 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3574 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3575 len1 = selEnd - selBegin;
3578 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3581 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3582 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3583 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3584 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3585 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3586 len2 = selEnd - selBegin;
3589 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3592 /* set text up to the limit, select all the text, then add a char */
3594 memset(text, 'W', textlimit);
3595 text[textlimit] = 0;
3596 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3597 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3598 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3599 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3600 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3601 result = strcmp(buffer, "A");
3602 ok(0 == result, "got string = \"%s\"\n", buffer);
3604 /* WM_SETTEXT not limited */
3606 memset(text, 'W', textlimit);
3607 text[textlimit] = 0;
3608 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3609 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3610 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3612 ok(10 == i, "expected 10 chars\n");
3613 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3614 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3616 /* try inserting more text at end */
3617 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3618 ok(0 == i, "WM_CHAR wasn't processed\n");
3619 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3621 ok(10 == i, "expected 10 chars, got %i\n", i);
3622 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3623 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3625 /* try inserting text at beginning */
3626 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3627 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3628 ok(0 == i, "WM_CHAR wasn't processed\n");
3629 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3631 ok(10 == i, "expected 10 chars, got %i\n", i);
3632 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3633 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3635 /* WM_CHAR is limited */
3637 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3638 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3639 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3640 ok(0 == i, "WM_CHAR wasn't processed\n");
3641 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3642 ok(0 == i, "WM_CHAR wasn't processed\n");
3643 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3645 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3647 DestroyWindow(hwndRichEdit);
3650 static void test_EM_GETLIMITTEXT(void)
3653 HWND hwndRichEdit = new_richedit(NULL);
3655 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3656 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3658 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3659 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3660 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3662 DestroyWindow(hwndRichEdit);
3665 static void test_WM_SETFONT(void)
3667 /* There is no invalid input or error conditions for this function.
3668 * NULL wParam and lParam just fall back to their default values
3669 * It should be noted that even if you use a gibberish name for your fonts
3670 * here, it will still work because the name is stored. They will display as
3671 * System, but will report their name to be whatever they were created as */
3673 HWND hwndRichEdit = new_richedit(NULL);
3674 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3675 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3676 FF_DONTCARE, "Marlett");
3677 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3678 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3679 FF_DONTCARE, "MS Sans Serif");
3680 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3681 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3682 FF_DONTCARE, "Courier");
3683 LOGFONTA sentLogFont;
3684 CHARFORMAT2A returnedCF2A;
3686 returnedCF2A.cbSize = sizeof(returnedCF2A);
3688 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3689 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
3690 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3692 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3693 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3694 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3695 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3697 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
3698 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3699 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3700 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3701 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3702 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3704 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
3705 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3706 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3707 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3708 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3709 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3711 /* This last test is special since we send in NULL. We clear the variables
3712 * and just compare to "System" instead of the sent in font name. */
3713 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3714 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3715 returnedCF2A.cbSize = sizeof(returnedCF2A);
3717 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
3718 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3719 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3720 ok (!strcmp("System",returnedCF2A.szFaceName),
3721 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3723 DestroyWindow(hwndRichEdit);
3727 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3732 const char** str = (const char**)dwCookie;
3733 int size = strlen(*str);
3734 if(size > 3) /* let's make it piecemeal for fun */
3741 memcpy(pbBuff, *str, *pcb);
3747 static void test_EM_GETMODIFY(void)
3749 HWND hwndRichEdit = new_richedit(NULL);
3752 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3754 'T', 'e', 'x', 't', 0};
3755 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3757 'O', 't', 'h', 'e', 'r',
3758 'T', 'e', 'x', 't', 0};
3759 const char* streamText = "hello world";
3764 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3765 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3766 FF_DONTCARE, "Courier");
3768 setText.codepage = 1200; /* no constant for unicode */
3769 setText.flags = ST_KEEPUNDO;
3772 /* modify flag shouldn't be set when richedit is first created */
3773 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3775 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
3777 /* setting modify flag should actually set it */
3778 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
3779 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3781 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
3783 /* clearing modify flag should actually clear it */
3784 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3785 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3787 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
3789 /* setting font doesn't change modify flag */
3790 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3791 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
3792 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3794 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
3796 /* setting text should set modify flag */
3797 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3798 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3799 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3801 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
3803 /* undo previous text doesn't reset modify flag */
3804 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3805 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3807 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
3809 /* set text with no flag to keep undo stack should not set modify flag */
3810 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3812 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3813 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3815 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
3817 /* WM_SETTEXT doesn't modify */
3818 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3819 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3820 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3822 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3824 /* clear the text */
3825 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3826 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3827 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3829 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3832 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3833 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3834 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3835 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3836 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3838 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3840 /* copy/paste text 1 */
3841 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3842 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3843 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3844 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3845 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3847 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3849 /* copy/paste text 2 */
3850 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3851 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3852 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3853 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3854 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3855 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3857 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3860 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3861 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3862 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3863 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3865 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3868 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3869 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3870 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
3871 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3873 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3875 /* set char format */
3876 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3877 cf2.cbSize = sizeof(CHARFORMAT2);
3878 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3880 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3881 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3882 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3883 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3884 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3885 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3887 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3889 /* set para format */
3890 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3891 pf2.cbSize = sizeof(PARAFORMAT2);
3892 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3894 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3895 pf2.wAlignment = PFA_RIGHT;
3896 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3897 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3899 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3902 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3903 es.dwCookie = (DWORD_PTR)&streamText;
3905 es.pfnCallback = test_EM_GETMODIFY_esCallback;
3906 SendMessage(hwndRichEdit, EM_STREAMIN,
3907 (WPARAM)(SF_TEXT), (LPARAM)&es);
3908 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3910 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
3912 DestroyWindow(hwndRichEdit);
3918 long expected_retval;
3919 int expected_getsel_start;
3920 int expected_getsel_end;
3921 int _exsetsel_todo_wine;
3922 int _getsel_todo_wine;
3925 const struct exsetsel_s exsetsel_tests[] = {
3927 {5, 10, 10, 5, 10, 0, 0},
3928 {15, 17, 17, 15, 17, 0, 0},
3929 /* test cpMax > strlen() */
3930 {0, 100, 18, 0, 18, 0, 1},
3931 /* test cpMin == cpMax */
3932 {5, 5, 5, 5, 5, 0, 0},
3933 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
3934 {-1, 0, 5, 5, 5, 0, 0},
3935 {-1, 17, 5, 5, 5, 0, 0},
3936 {-1, 18, 5, 5, 5, 0, 0},
3937 /* test cpMin < 0 && cpMax < 0 */
3938 {-1, -1, 17, 17, 17, 0, 0},
3939 {-4, -5, 17, 17, 17, 0, 0},
3940 /* test cMin >=0 && cpMax < 0 (bug 6814) */
3941 {0, -1, 18, 0, 18, 0, 1},
3942 {17, -5, 18, 17, 18, 0, 1},
3943 {18, -3, 17, 17, 17, 0, 0},
3944 /* test if cpMin > cpMax */
3945 {15, 19, 18, 15, 18, 0, 1},
3946 {19, 15, 18, 15, 18, 0, 1}
3949 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
3954 cr.cpMin = setsel->min;
3955 cr.cpMax = setsel->max;
3956 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
3958 if (setsel->_exsetsel_todo_wine) {
3960 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3963 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3966 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
3968 if (setsel->_getsel_todo_wine) {
3970 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);
3973 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);
3977 static void test_EM_EXSETSEL(void)
3979 HWND hwndRichEdit = new_richedit(NULL);
3981 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
3983 /* sending some text to the window */
3984 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3985 /* 01234567890123456*/
3988 for (i = 0; i < num_tests; i++) {
3989 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
3992 DestroyWindow(hwndRichEdit);
3995 static void test_EM_REPLACESEL(int redraw)
3997 HWND hwndRichEdit = new_richedit(NULL);
3998 char buffer[1024] = {0};
4003 /* sending some text to the window */
4004 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4005 /* 01234567890123456*/
4008 /* FIXME add more tests */
4009 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4010 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
4011 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4012 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4013 r = strcmp(buffer, "testing");
4014 ok(0 == r, "expected %d, got %d\n", 0, r);
4016 DestroyWindow(hwndRichEdit);
4018 hwndRichEdit = new_richedit(NULL);
4020 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4021 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4023 /* Test behavior with carriage returns and newlines */
4024 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4025 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4026 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4027 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4028 r = strcmp(buffer, "RichEdit1");
4029 ok(0 == r, "expected %d, got %d\n", 0, r);
4031 getText.codepage = CP_ACP;
4032 getText.flags = GT_DEFAULT;
4033 getText.lpDefaultChar = NULL;
4034 getText.lpUsedDefChar = NULL;
4035 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4036 ok(strcmp(buffer, "RichEdit1") == 0,
4037 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4039 /* Test number of lines reported after EM_REPLACESEL */
4040 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4041 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4043 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4044 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4045 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4046 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4047 r = strcmp(buffer, "RichEdit1\r\n");
4048 ok(0 == r, "expected %d, got %d\n", 0, r);
4050 getText.codepage = CP_ACP;
4051 getText.flags = GT_DEFAULT;
4052 getText.lpDefaultChar = NULL;
4053 getText.lpUsedDefChar = NULL;
4054 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4055 ok(strcmp(buffer, "RichEdit1\r") == 0,
4056 "EM_GETTEXTEX returned incorrect string\n");
4058 /* Test number of lines reported after EM_REPLACESEL */
4059 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4060 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4062 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4063 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4064 returns the number of characters *inserted* into the control (after
4065 required conversions), but WinXP's riched20 returns the number of
4066 characters interpreted from the original lParam. Wine's builtin riched20
4067 implements the WinXP behavior.
4069 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4070 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4071 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4072 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4074 /* Test number of lines reported after EM_REPLACESEL */
4075 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4076 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4078 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4079 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4080 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4081 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4083 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4084 r = strcmp(buffer, "RichEdit1\r\n");
4085 ok(0 == r, "expected %d, got %d\n", 0, r);
4087 getText.codepage = CP_ACP;
4088 getText.flags = GT_DEFAULT;
4089 getText.lpDefaultChar = NULL;
4090 getText.lpUsedDefChar = NULL;
4091 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4092 ok(strcmp(buffer, "RichEdit1\r") == 0,
4093 "EM_GETTEXTEX returned incorrect string\n");
4095 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4096 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4097 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4098 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4100 /* The following tests show that richedit should handle the special \r\r\n
4101 sequence by turning it into a single space on insertion. However,
4102 EM_REPLACESEL on WinXP returns the number of characters in the original
4106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4107 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4108 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4109 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4110 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4111 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4112 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4114 /* Test the actual string */
4116 getText.codepage = CP_ACP;
4117 getText.flags = GT_DEFAULT;
4118 getText.lpDefaultChar = NULL;
4119 getText.lpUsedDefChar = NULL;
4120 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4121 ok(strcmp(buffer, "\r\r") == 0,
4122 "EM_GETTEXTEX returned incorrect string\n");
4124 /* Test number of lines reported after EM_REPLACESEL */
4125 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4126 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4128 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4129 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4130 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4131 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4132 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4133 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4134 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4135 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4137 /* Test the actual string */
4139 getText.codepage = CP_ACP;
4140 getText.flags = GT_DEFAULT;
4141 getText.lpDefaultChar = NULL;
4142 getText.lpUsedDefChar = NULL;
4143 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4144 ok(strcmp(buffer, " ") == 0,
4145 "EM_GETTEXTEX returned incorrect string\n");
4147 /* Test number of lines reported after EM_REPLACESEL */
4148 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4149 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4151 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4152 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4153 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4154 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4155 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4156 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4157 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4158 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4160 /* Test the actual string */
4162 getText.codepage = CP_ACP;
4163 getText.flags = GT_DEFAULT;
4164 getText.lpDefaultChar = NULL;
4165 getText.lpUsedDefChar = NULL;
4166 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4167 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4168 "EM_GETTEXTEX returned incorrect string\n");
4170 /* Test number of lines reported after EM_REPLACESEL */
4171 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4172 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4174 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4175 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4176 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4177 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4178 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4179 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4180 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4181 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4183 /* Test the actual string */
4185 getText.codepage = CP_ACP;
4186 getText.flags = GT_DEFAULT;
4187 getText.lpDefaultChar = NULL;
4188 getText.lpUsedDefChar = NULL;
4189 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4190 ok(strcmp(buffer, " \r") == 0,
4191 "EM_GETTEXTEX returned incorrect string\n");
4193 /* Test number of lines reported after EM_REPLACESEL */
4194 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4195 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4197 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4198 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4199 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4200 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4201 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4202 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4203 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4204 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4206 /* Test the actual string */
4208 getText.codepage = CP_ACP;
4209 getText.flags = GT_DEFAULT;
4210 getText.lpDefaultChar = NULL;
4211 getText.lpUsedDefChar = NULL;
4212 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4213 ok(strcmp(buffer, " \r\r") == 0,
4214 "EM_GETTEXTEX returned incorrect string\n");
4216 /* Test number of lines reported after EM_REPLACESEL */
4217 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4218 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4220 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4221 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4222 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4223 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4224 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4225 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4226 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4227 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4229 /* Test the actual string */
4231 getText.codepage = CP_ACP;
4232 getText.flags = GT_DEFAULT;
4233 getText.lpDefaultChar = NULL;
4234 getText.lpUsedDefChar = NULL;
4235 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4236 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4237 "EM_GETTEXTEX returned incorrect string\n");
4239 /* Test number of lines reported after EM_REPLACESEL */
4240 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4241 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4243 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4244 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4245 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4246 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4247 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4248 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4249 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4251 /* Test the actual string */
4253 getText.codepage = CP_ACP;
4254 getText.flags = GT_DEFAULT;
4255 getText.lpDefaultChar = NULL;
4256 getText.lpUsedDefChar = NULL;
4257 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4258 ok(strcmp(buffer, "\r\r") == 0,
4259 "EM_GETTEXTEX returned incorrect string\n");
4261 /* Test number of lines reported after EM_REPLACESEL */
4262 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4263 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4265 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4266 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4267 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4268 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4269 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4270 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4271 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4272 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4274 /* Test the actual string */
4276 getText.codepage = CP_ACP;
4277 getText.flags = GT_DEFAULT;
4278 getText.lpDefaultChar = NULL;
4279 getText.lpUsedDefChar = NULL;
4280 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4281 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4282 "EM_GETTEXTEX returned incorrect string\n");
4284 /* Test number of lines reported after EM_REPLACESEL */
4285 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4286 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4289 /* This is needed to avoid interferring with keybd_event calls
4290 * on other tests that simulate keyboard events. */
4291 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4293 DestroyWindow(hwndRichEdit);
4296 static void test_WM_PASTE(void)
4299 char buffer[1024] = {0};
4300 const char* text1 = "testing paste\r";
4301 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4302 const char* text1_after = "testing paste\r\n";
4303 const char* text2 = "testing paste\r\rtesting paste";
4304 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4305 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4306 HWND hwndRichEdit = new_richedit(NULL);
4308 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
4309 messages, probably because it inspects the keyboard state itself.
4310 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
4313 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4314 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4315 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4316 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4317 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4319 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4320 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4322 SEND_CTRL_C(hwndRichEdit); /* Copy */
4323 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4324 SEND_CTRL_V(hwndRichEdit); /* Paste */
4325 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4326 /* Pasted text should be visible at this step */
4327 result = strcmp(text1_step1, buffer);
4329 "test paste: strcmp = %i\n", result);
4330 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4331 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4332 /* Text should be the same as before (except for \r -> \r\n conversion) */
4333 result = strcmp(text1_after, buffer);
4335 "test paste: strcmp = %i\n", result);
4337 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4338 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4339 SEND_CTRL_C(hwndRichEdit); /* Copy */
4340 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4341 SEND_CTRL_V(hwndRichEdit); /* Paste */
4342 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4343 /* Pasted text should be visible at this step */
4344 result = strcmp(text3, buffer);
4346 "test paste: strcmp = %i\n", result);
4347 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4348 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4349 /* Text should be the same as before (except for \r -> \r\n conversion) */
4350 result = strcmp(text2_after, buffer);
4352 "test paste: strcmp = %i\n", result);
4353 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4354 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4355 /* Text should revert to post-paste state */
4356 result = strcmp(buffer,text3);
4358 "test paste: strcmp = %i\n", result);
4360 DestroyWindow(hwndRichEdit);
4363 static void test_EM_FORMATRANGE(void)
4368 HWND hwndRichEdit = new_richedit(NULL);
4370 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4372 hdc = GetDC(hwndRichEdit);
4373 ok(hdc != NULL, "Could not get HDC\n");
4375 fr.hdc = fr.hdcTarget = hdc;
4376 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4377 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4378 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4382 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4384 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4387 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4389 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
4395 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4397 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4400 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4402 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4405 DestroyWindow(hwndRichEdit);
4408 static int nCallbackCount = 0;
4410 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4413 const char text[] = {'t','e','s','t'};
4415 if (sizeof(text) <= cb)
4417 if ((int)dwCookie != nCallbackCount)
4423 memcpy (pbBuff, text, sizeof(text));
4424 *pcb = sizeof(text);
4431 return 1; /* indicates callback failed */
4434 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4439 const char** str = (const char**)dwCookie;
4440 int size = strlen(*str);
4446 memcpy(pbBuff, *str, *pcb);
4452 struct StringWithLength {
4457 /* This callback is used to handled the null characters in a string. */
4458 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4463 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4464 int size = str->length;
4470 memcpy(pbBuff, str->buffer, *pcb);
4471 str->buffer += *pcb;
4472 str->length -= *pcb;
4477 static void test_EM_STREAMIN(void)
4479 HWND hwndRichEdit = new_richedit(NULL);
4482 char buffer[1024] = {0};
4484 const char * streamText0 = "{\\rtf1 TestSomeText}";
4485 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4486 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4488 const char * streamText1 =
4489 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
4490 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
4493 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4494 const char * streamText2 =
4495 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
4496 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
4497 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
4498 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
4499 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
4500 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
4501 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4503 const char * streamText3 = "RichEdit1";
4505 struct StringWithLength cookieForStream4;
4506 const char * streamText4 =
4507 "This text just needs to be long enough to cause run to be split onto "\
4508 "two separate lines and make sure the null terminating character is "\
4509 "handled properly.\0";
4510 int length4 = strlen(streamText4) + 1;
4511 cookieForStream4.buffer = (char *)streamText4;
4512 cookieForStream4.length = length4;
4514 /* Minimal test without \par at the end */
4515 es.dwCookie = (DWORD_PTR)&streamText0;
4517 es.pfnCallback = test_EM_STREAMIN_esCallback;
4518 SendMessage(hwndRichEdit, EM_STREAMIN,
4519 (WPARAM)(SF_RTF), (LPARAM)&es);
4521 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4523 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4524 result = strcmp (buffer,"TestSomeText");
4526 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4527 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4529 /* Native richedit 2.0 ignores last \par */
4530 es.dwCookie = (DWORD_PTR)&streamText0a;
4532 es.pfnCallback = test_EM_STREAMIN_esCallback;
4533 SendMessage(hwndRichEdit, EM_STREAMIN,
4534 (WPARAM)(SF_RTF), (LPARAM)&es);
4536 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4538 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4539 result = strcmp (buffer,"TestSomeText");
4541 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4542 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4544 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4545 es.dwCookie = (DWORD_PTR)&streamText0b;
4547 es.pfnCallback = test_EM_STREAMIN_esCallback;
4548 SendMessage(hwndRichEdit, EM_STREAMIN,
4549 (WPARAM)(SF_RTF), (LPARAM)&es);
4551 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4553 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4554 result = strcmp (buffer,"TestSomeText\r\n");
4556 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4557 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4559 es.dwCookie = (DWORD_PTR)&streamText1;
4561 es.pfnCallback = test_EM_STREAMIN_esCallback;
4562 SendMessage(hwndRichEdit, EM_STREAMIN,
4563 (WPARAM)(SF_RTF), (LPARAM)&es);
4565 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4567 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4568 result = strcmp (buffer,"TestSomeText");
4570 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4571 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4573 es.dwCookie = (DWORD_PTR)&streamText2;
4575 SendMessage(hwndRichEdit, EM_STREAMIN,
4576 (WPARAM)(SF_RTF), (LPARAM)&es);
4578 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4580 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4581 ok (strlen(buffer) == 0,
4582 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4583 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4585 es.dwCookie = (DWORD_PTR)&streamText3;
4587 SendMessage(hwndRichEdit, EM_STREAMIN,
4588 (WPARAM)(SF_RTF), (LPARAM)&es);
4590 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4592 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4593 ok (strlen(buffer) == 0,
4594 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4595 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4597 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4599 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4600 SendMessage(hwndRichEdit, EM_STREAMIN,
4601 (WPARAM)(SF_TEXT), (LPARAM)&es);
4603 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4604 ok (result == length4,
4605 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4606 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4608 DestroyWindow(hwndRichEdit);
4611 static void test_EM_StreamIn_Undo(void)
4613 /* The purpose of this test is to determine when a EM_StreamIn should be
4614 * undoable. This is important because WM_PASTE currently uses StreamIn and
4615 * pasting should always be undoable but streaming isn't always.
4618 * StreamIn plain text without SFF_SELECTION.
4619 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4620 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4621 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4622 * Feel free to add tests for other text modes or StreamIn things.
4626 HWND hwndRichEdit = new_richedit(NULL);
4629 char buffer[1024] = {0};
4630 const char randomtext[] = "Some text";
4632 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4634 /* StreamIn, no SFF_SELECTION */
4635 es.dwCookie = nCallbackCount;
4636 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4637 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4638 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4639 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4640 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4641 result = strcmp (buffer,"test");
4643 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4645 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4646 ok (result == FALSE,
4647 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4649 /* StreamIn, SFF_SELECTION, but nothing selected */
4650 es.dwCookie = nCallbackCount;
4651 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4652 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4653 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4654 SendMessage(hwndRichEdit, EM_STREAMIN,
4655 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4656 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4657 result = strcmp (buffer,"testSome text");
4659 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4661 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4663 "EM_STREAMIN with SFF_SELECTION but no selection set "
4664 "should create an undo\n");
4666 /* StreamIn, SFF_SELECTION, with a selection */
4667 es.dwCookie = nCallbackCount;
4668 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4669 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4670 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
4671 SendMessage(hwndRichEdit, EM_STREAMIN,
4672 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4673 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4674 result = strcmp (buffer,"Sometesttext");
4676 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4678 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4680 "EM_STREAMIN with SFF_SELECTION and selection set "
4681 "should create an undo\n");
4683 DestroyWindow(hwndRichEdit);
4686 static BOOL is_em_settextex_supported(HWND hwnd)
4688 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
4689 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
4692 static void test_unicode_conversions(void)
4694 static const WCHAR tW[] = {'t',0};
4695 static const WCHAR teW[] = {'t','e',0};
4696 static const WCHAR textW[] = {'t','e','s','t',0};
4697 static const char textA[] = "test";
4701 int is_win9x, em_settextex_supported, ret;
4703 is_win9x = GetVersion() & 0x80000000;
4705 #define set_textA(hwnd, wm_set_text, txt) \
4707 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
4708 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4709 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4710 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4711 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
4713 #define expect_textA(hwnd, wm_get_text, txt) \
4715 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4716 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4717 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4718 memset(bufA, 0xAA, sizeof(bufA)); \
4719 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4720 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4721 ret = lstrcmpA(bufA, txt); \
4722 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
4725 #define set_textW(hwnd, wm_set_text, txt) \
4727 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
4728 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4729 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4730 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4731 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
4733 #define expect_textW(hwnd, wm_get_text, txt) \
4735 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
4736 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4737 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4738 memset(bufW, 0xAA, sizeof(bufW)); \
4741 assert(wm_get_text == EM_GETTEXTEX); \
4742 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4743 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4747 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4748 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
4750 ret = lstrcmpW(bufW, txt); \
4751 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
4753 #define expect_empty(hwnd, wm_get_text) \
4755 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4756 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4757 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4758 memset(bufA, 0xAA, sizeof(bufA)); \
4759 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4760 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
4761 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
4764 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4765 0, 0, 200, 60, 0, 0, 0, 0);
4766 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4768 ret = IsWindowUnicode(hwnd);
4770 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
4772 ok(ret, "RichEdit20W should be unicode under NT\n");
4774 /* EM_SETTEXTEX is supported starting from version 3.0 */
4775 em_settextex_supported = is_em_settextex_supported(hwnd);
4776 trace("EM_SETTEXTEX is %ssupported on this platform\n",
4777 em_settextex_supported ? "" : "NOT ");
4779 expect_empty(hwnd, WM_GETTEXT);
4780 expect_empty(hwnd, EM_GETTEXTEX);
4782 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
4783 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4784 expect_textA(hwnd, WM_GETTEXT, "t");
4785 expect_textA(hwnd, EM_GETTEXTEX, "t");
4786 expect_textW(hwnd, EM_GETTEXTEX, tW);
4788 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
4789 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4790 expect_textA(hwnd, WM_GETTEXT, "te");
4791 expect_textA(hwnd, EM_GETTEXTEX, "te");
4792 expect_textW(hwnd, EM_GETTEXTEX, teW);
4794 set_textA(hwnd, WM_SETTEXT, NULL);
4795 expect_empty(hwnd, WM_GETTEXT);
4796 expect_empty(hwnd, EM_GETTEXTEX);
4799 set_textA(hwnd, WM_SETTEXT, textW);
4801 set_textA(hwnd, WM_SETTEXT, textA);
4802 expect_textA(hwnd, WM_GETTEXT, textA);
4803 expect_textA(hwnd, EM_GETTEXTEX, textA);
4804 expect_textW(hwnd, EM_GETTEXTEX, textW);
4806 if (em_settextex_supported)
4808 set_textA(hwnd, EM_SETTEXTEX, textA);
4809 expect_textA(hwnd, WM_GETTEXT, textA);
4810 expect_textA(hwnd, EM_GETTEXTEX, textA);
4811 expect_textW(hwnd, EM_GETTEXTEX, textW);
4816 set_textW(hwnd, WM_SETTEXT, textW);
4817 expect_textW(hwnd, WM_GETTEXT, textW);
4818 expect_textA(hwnd, WM_GETTEXT, textA);
4819 expect_textW(hwnd, EM_GETTEXTEX, textW);
4820 expect_textA(hwnd, EM_GETTEXTEX, textA);
4822 if (em_settextex_supported)
4824 set_textW(hwnd, EM_SETTEXTEX, textW);
4825 expect_textW(hwnd, WM_GETTEXT, textW);
4826 expect_textA(hwnd, WM_GETTEXT, textA);
4827 expect_textW(hwnd, EM_GETTEXTEX, textW);
4828 expect_textA(hwnd, EM_GETTEXTEX, textA);
4831 DestroyWindow(hwnd);
4833 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
4834 0, 0, 200, 60, 0, 0, 0, 0);
4835 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4837 ret = IsWindowUnicode(hwnd);
4838 ok(!ret, "RichEdit20A should NOT be unicode\n");
4840 set_textA(hwnd, WM_SETTEXT, textA);
4841 expect_textA(hwnd, WM_GETTEXT, textA);
4842 expect_textA(hwnd, EM_GETTEXTEX, textA);
4843 expect_textW(hwnd, EM_GETTEXTEX, textW);
4845 if (em_settextex_supported)
4847 set_textA(hwnd, EM_SETTEXTEX, textA);
4848 expect_textA(hwnd, WM_GETTEXT, textA);
4849 expect_textA(hwnd, EM_GETTEXTEX, textA);
4850 expect_textW(hwnd, EM_GETTEXTEX, textW);
4855 set_textW(hwnd, WM_SETTEXT, textW);
4856 expect_textW(hwnd, WM_GETTEXT, textW);
4857 expect_textA(hwnd, WM_GETTEXT, textA);
4858 expect_textW(hwnd, EM_GETTEXTEX, textW);
4859 expect_textA(hwnd, EM_GETTEXTEX, textA);
4861 if (em_settextex_supported)
4863 set_textW(hwnd, EM_SETTEXTEX, textW);
4864 expect_textW(hwnd, WM_GETTEXT, textW);
4865 expect_textA(hwnd, WM_GETTEXT, textA);
4866 expect_textW(hwnd, EM_GETTEXTEX, textW);
4867 expect_textA(hwnd, EM_GETTEXTEX, textA);
4870 DestroyWindow(hwnd);
4873 static void test_WM_CHAR(void)
4877 const char * char_list = "abc\rabc\r";
4878 const char * expected_content_single = "abcabc";
4879 const char * expected_content_multi = "abc\r\nabc\r\n";
4880 char buffer[64] = {0};
4883 /* single-line control must IGNORE carriage returns */
4884 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4885 0, 0, 200, 60, 0, 0, 0, 0);
4886 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4889 while (*p != '\0') {
4890 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4891 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4892 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4893 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4897 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4898 ret = strcmp(buffer, expected_content_single);
4899 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4901 DestroyWindow(hwnd);
4903 /* multi-line control inserts CR normally */
4904 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4905 0, 0, 200, 60, 0, 0, 0, 0);
4906 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4909 while (*p != '\0') {
4910 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4911 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4912 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4913 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4917 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4918 ret = strcmp(buffer, expected_content_multi);
4919 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4921 DestroyWindow(hwnd);
4924 static void test_EM_GETTEXTLENGTHEX(void)
4927 GETTEXTLENGTHEX gtl;
4929 const char * base_string = "base string";
4930 const char * test_string = "a\nb\n\n\r\n";
4931 const char * test_string_after = "a";
4932 const char * test_string_2 = "a\rtest\rstring";
4933 char buffer[64] = {0};
4936 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4937 0, 0, 200, 60, 0, 0, 0, 0);
4938 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4940 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4941 gtl.codepage = CP_ACP;
4942 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4943 ok(ret == 0, "ret %d\n",ret);
4945 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4946 gtl.codepage = CP_ACP;
4947 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4948 ok(ret == 0, "ret %d\n",ret);
4950 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4952 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4953 gtl.codepage = CP_ACP;
4954 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4955 ok(ret == strlen(base_string), "ret %d\n",ret);
4957 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4958 gtl.codepage = CP_ACP;
4959 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4960 ok(ret == strlen(base_string), "ret %d\n",ret);
4962 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
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 == 1, "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 == 1, "ret %d\n",ret);
4974 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4975 ret = strcmp(buffer, test_string_after);
4976 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4978 DestroyWindow(hwnd);
4981 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
4982 0, 0, 200, 60, 0, 0, 0, 0);
4983 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4985 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4986 gtl.codepage = CP_ACP;
4987 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4988 ok(ret == 0, "ret %d\n",ret);
4990 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4991 gtl.codepage = CP_ACP;
4992 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4993 ok(ret == 0, "ret %d\n",ret);
4995 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4997 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4998 gtl.codepage = CP_ACP;
4999 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5000 ok(ret == strlen(base_string), "ret %d\n",ret);
5002 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5003 gtl.codepage = CP_ACP;
5004 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5005 ok(ret == strlen(base_string), "ret %d\n",ret);
5007 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5009 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5010 gtl.codepage = CP_ACP;
5011 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5012 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5014 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5015 gtl.codepage = CP_ACP;
5016 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5017 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5019 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5021 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5022 gtl.codepage = CP_ACP;
5023 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5024 ok(ret == 10, "ret %d\n",ret);
5026 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5027 gtl.codepage = CP_ACP;
5028 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5029 ok(ret == 6, "ret %d\n",ret);
5031 DestroyWindow(hwnd);
5035 /* globals that parent and child access when checking event masks & notifications */
5036 static HWND eventMaskEditHwnd = 0;
5037 static int queriedEventMask;
5038 static int watchForEventMask = 0;
5040 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5041 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5043 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5045 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5047 return DefWindowProcA(hwnd, message, wParam, lParam);
5050 /* test event masks in combination with WM_COMMAND */
5051 static void test_eventMask(void)
5056 const char text[] = "foo bar\n";
5059 /* register class to capture WM_COMMAND */
5061 cls.lpfnWndProc = ParentMsgCheckProcA;
5064 cls.hInstance = GetModuleHandleA(0);
5066 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5067 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5068 cls.lpszMenuName = NULL;
5069 cls.lpszClassName = "EventMaskParentClass";
5070 if(!RegisterClassA(&cls)) assert(0);
5072 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5073 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5074 ok (parent != 0, "Failed to create parent window\n");
5076 eventMaskEditHwnd = new_richedit(parent);
5077 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5079 eventMask = ENM_CHANGE | ENM_UPDATE;
5080 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5081 ok(ret == ENM_NONE, "wrong event mask\n");
5082 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5083 ok(ret == eventMask, "failed to set event mask\n");
5085 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5086 queriedEventMask = 0; /* initialize to something other than we expect */
5087 watchForEventMask = EN_CHANGE;
5088 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5089 ok(ret == TRUE, "failed to set text\n");
5090 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5091 notification in response to WM_SETTEXT */
5092 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5093 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5097 static int received_WM_NOTIFY = 0;
5098 static int modify_at_WM_NOTIFY = 0;
5099 static HWND hwndRichedit_WM_NOTIFY;
5101 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5103 if(message == WM_NOTIFY)
5105 received_WM_NOTIFY = 1;
5106 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5108 return DefWindowProcA(hwnd, message, wParam, lParam);
5111 static void test_WM_NOTIFY(void)
5117 /* register class to capture WM_NOTIFY */
5119 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5122 cls.hInstance = GetModuleHandleA(0);
5124 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5125 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5126 cls.lpszMenuName = NULL;
5127 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5128 if(!RegisterClassA(&cls)) assert(0);
5130 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5131 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5132 ok (parent != 0, "Failed to create parent window\n");
5134 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5135 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5137 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5139 /* Notifications for selection change should only be sent when selection
5140 actually changes. EM_SETCHARFORMAT is one message that calls
5141 ME_CommitUndo, which should check whether message should be sent */
5142 received_WM_NOTIFY = 0;
5143 cf2.cbSize = sizeof(CHARFORMAT2);
5144 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5146 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5147 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5148 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5149 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5151 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5153 received_WM_NOTIFY = 0;
5154 modify_at_WM_NOTIFY = 0;
5155 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5156 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5157 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5159 received_WM_NOTIFY = 0;
5160 modify_at_WM_NOTIFY = 0;
5161 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5162 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5164 received_WM_NOTIFY = 0;
5165 modify_at_WM_NOTIFY = 0;
5166 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5167 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5168 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5170 DestroyWindow(hwndRichedit_WM_NOTIFY);
5171 DestroyWindow(parent);
5174 static void test_undo_coalescing(void)
5178 char buffer[64] = {0};
5180 /* multi-line control inserts CR normally */
5181 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5182 0, 0, 200, 60, 0, 0, 0, 0);
5183 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5185 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5186 ok (result == FALSE, "Can undo after window creation.\n");
5187 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5188 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5189 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5190 ok (result == FALSE, "Can redo after window creation.\n");
5191 result = SendMessage(hwnd, EM_REDO, 0, 0);
5192 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5194 /* Test the effect of arrows keys during typing on undo transactions*/
5195 simulate_typing_characters(hwnd, "one two three");
5196 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5197 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5198 simulate_typing_characters(hwnd, " four five six");
5200 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5201 ok (result == FALSE, "Can redo before anything is undone.\n");
5202 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5203 ok (result == TRUE, "Cannot undo typed characters.\n");
5204 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5205 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5206 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5207 ok (result == TRUE, "Cannot redo after undo.\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 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5213 ok (result == TRUE, "Cannot undo typed characters.\n");
5214 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5215 ok (result == TRUE, "Failed to undo typed characters.\n");
5216 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5217 result = strcmp(buffer, "");
5218 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5220 /* Test the effect of focus changes during typing on undo transactions*/
5221 simulate_typing_characters(hwnd, "one two three");
5222 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5223 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5224 SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
5225 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
5226 simulate_typing_characters(hwnd, " four five six");
5227 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5228 ok (result == TRUE, "Failed to undo typed characters.\n");
5229 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5230 result = strcmp(buffer, "one two three");
5231 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5233 /* Test the effect of the back key during typing on undo transactions */
5234 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5235 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5236 ok (result == TRUE, "Failed to clear the text.\n");
5237 simulate_typing_characters(hwnd, "one two threa");
5238 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5239 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5240 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5241 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5242 simulate_typing_characters(hwnd, "e four five six");
5243 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5244 ok (result == TRUE, "Failed to undo typed characters.\n");
5245 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5246 result = strcmp(buffer, "");
5247 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5249 /* Test the effect of the delete key during typing on undo transactions */
5250 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5251 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5252 ok(result == TRUE, "Failed to set the text.\n");
5253 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5254 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5255 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5256 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5257 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5258 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5259 ok (result == TRUE, "Failed to undo typed characters.\n");
5260 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5261 result = strcmp(buffer, "acd");
5262 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5263 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5264 ok (result == TRUE, "Failed to undo typed characters.\n");
5265 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5266 result = strcmp(buffer, "abcd");
5267 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5269 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5270 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5271 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5272 ok (result == TRUE, "Failed to clear the text.\n");
5273 simulate_typing_characters(hwnd, "one two three");
5274 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5275 ok (result == 0, "expected %d but got %d\n", 0, result);
5276 simulate_typing_characters(hwnd, " four five six");
5277 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5278 ok (result == TRUE, "Failed to undo typed characters.\n");
5279 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5280 result = strcmp(buffer, "one two three");
5281 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5282 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5283 ok (result == TRUE, "Failed to undo typed characters.\n");
5284 ok (result == TRUE, "Failed to undo typed characters.\n");
5285 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5286 result = strcmp(buffer, "");
5287 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5289 DestroyWindow(hwnd);
5292 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5293 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5295 static void test_word_movement(void)
5299 int sel_start, sel_end;
5301 /* multi-line control inserts CR normally */
5302 hwnd = new_richedit(NULL);
5304 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5305 ok (result == TRUE, "Failed to clear the text.\n");
5306 SendMessage(hwnd, EM_SETSEL, 0, 0);
5307 /* |one two three */
5309 SEND_CTRL_RIGHT(hwnd);
5310 /* one |two three */
5311 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5312 ok(sel_start == sel_end, "Selection should be empty\n");
5313 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5315 SEND_CTRL_RIGHT(hwnd);
5316 /* one two |three */
5317 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5318 ok(sel_start == sel_end, "Selection should be empty\n");
5319 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5321 SEND_CTRL_LEFT(hwnd);
5322 /* one |two three */
5323 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5324 ok(sel_start == sel_end, "Selection should be empty\n");
5325 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5327 SEND_CTRL_LEFT(hwnd);
5328 /* |one two three */
5329 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5330 ok(sel_start == sel_end, "Selection should be empty\n");
5331 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5333 SendMessage(hwnd, EM_SETSEL, 8, 8);
5334 /* one two | three */
5335 SEND_CTRL_RIGHT(hwnd);
5336 /* one two |three */
5337 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5338 ok(sel_start == sel_end, "Selection should be empty\n");
5339 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5341 SendMessage(hwnd, EM_SETSEL, 11, 11);
5342 /* one two th|ree */
5343 SEND_CTRL_LEFT(hwnd);
5344 /* one two |three */
5345 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5346 ok(sel_start == sel_end, "Selection should be empty\n");
5347 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5349 DestroyWindow(hwnd);
5352 static void test_EM_CHARFROMPOS(void)
5360 /* multi-line control inserts CR normally */
5361 hwnd = new_richedit(NULL);
5362 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5363 (LPARAM)"one two three four five six seven");
5365 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5366 ok(result == 0, "expected character index of 0 but got %d\n", result);
5368 DestroyWindow(hwnd);
5371 START_TEST( editor )
5376 /* Must explicitly LoadLibrary(). The test has no references to functions in
5377 * RICHED20.DLL, so the linker doesn't actually link to it. */
5378 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
5379 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
5383 test_EM_POSFROMCHAR();
5384 test_EM_SCROLLCARET();
5386 test_scrollbar_visibility();
5388 test_EM_LINELENGTH();
5389 test_EM_SETCHARFORMAT();
5390 test_EM_SETTEXTMODE();
5391 test_TM_PLAINTEXT();
5392 test_EM_SETOPTIONS();
5394 test_EM_GETTEXTRANGE();
5395 test_EM_GETSELTEXT();
5396 test_EM_SETUNDOLIMIT();
5398 test_EM_SETTEXTEX();
5399 test_EM_LIMITTEXT();
5400 test_EM_EXLIMITTEXT();
5401 test_EM_GETLIMITTEXT();
5403 test_EM_GETMODIFY();
5407 test_EM_STREAMOUT();
5408 test_EM_StreamIn_Undo();
5409 test_EM_FORMATRANGE();
5410 test_unicode_conversions();
5411 test_EM_GETTEXTLENGTHEX();
5412 test_EM_REPLACESEL(1);
5413 test_EM_REPLACESEL(0);
5415 test_EM_AUTOURLDETECT();
5417 test_undo_coalescing();
5418 test_word_movement();
5419 test_EM_CHARFROMPOS();
5420 test_SETPARAFORMAT();
5422 /* Set the environment variable WINETEST_RICHED20 to keep windows
5423 * responsive and open for 30 seconds. This is useful for debugging.
5425 * The message pump uses PeekMessage() to empty the queue and then sleeps for
5426 * 50ms before retrying the queue. */
5427 end = time(NULL) + 30;
5428 if (getenv( "WINETEST_RICHED20" )) {
5429 while (time(NULL) < end) {
5430 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
5431 TranslateMessage(&msg);
5432 DispatchMessage(&msg);
5439 OleFlushClipboard();
5440 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());