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 const char haystack[] = "WINEWine wineWine wine WineWine";
63 struct find_s find_tests[] = {
64 /* Find in empty text */
65 {0, -1, "foo", FR_DOWN, -1, 0},
66 {0, -1, "foo", 0, -1, 0},
67 {0, -1, "", FR_DOWN, -1, 0},
68 {20, 5, "foo", FR_DOWN, -1, 0},
69 {5, 20, "foo", FR_DOWN, -1, 0}
72 struct find_s find_tests2[] = {
74 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
75 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
77 /* Subsequent finds */
78 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
79 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
80 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
81 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
84 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
85 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
86 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
88 /* Case-insensitive */
89 {1, 31, "wInE", FR_DOWN, 4, 0},
90 {1, 31, "Wine", FR_DOWN, 4, 0},
92 /* High-to-low ranges */
93 {20, 5, "Wine", FR_DOWN, -1, 0},
94 {2, 1, "Wine", FR_DOWN, -1, 0},
95 {30, 29, "Wine", FR_DOWN, -1, 0},
96 {20, 5, "Wine", 0, 13, 0},
99 {5, 10, "", FR_DOWN, -1, 0},
100 {10, 5, "", FR_DOWN, -1, 0},
101 {0, -1, "", FR_DOWN, -1, 0},
102 {10, 5, "", 0, -1, 0},
104 /* Whole-word search */
105 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
106 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
107 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
108 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
109 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
110 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
111 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
114 {5, 200, "XXX", FR_DOWN, -1, 0},
115 {-20, 20, "Wine", FR_DOWN, -1, 0},
116 {-20, 20, "Wine", FR_DOWN, -1, 0},
117 {-15, -20, "Wine", FR_DOWN, -1, 0},
118 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
120 /* Check the case noted in bug 4479 where matches at end aren't recognized */
121 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
122 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
123 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
124 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
125 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
127 /* The backwards case of bug 4479; bounds look right
128 * Fails because backward find is wrong */
129 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
130 {0, 20, "WINE", FR_MATCHCASE, -1, 0}
133 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
136 memset(&ft, 0, sizeof(ft));
137 ft.chrg.cpMin = f->start;
138 ft.chrg.cpMax = f->end;
139 ft.lpstrText = f->needle;
140 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
141 ok(findloc == f->expected_loc,
142 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
143 name, id, f->needle, f->start, f->end, f->flags, findloc);
146 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
150 memset(&ft, 0, sizeof(ft));
151 ft.chrg.cpMin = f->start;
152 ft.chrg.cpMax = f->end;
153 ft.lpstrText = f->needle;
154 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
155 ok(findloc == f->expected_loc,
156 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
157 name, id, f->needle, f->start, f->end, f->flags, findloc);
158 ok(ft.chrgText.cpMin == f->expected_loc,
159 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
160 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
161 ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
162 : f->expected_loc + strlen(f->needle)),
163 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n",
164 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
167 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
172 for (i = 0; i < num_tests; i++) {
173 if (find[i]._todo_wine) {
175 check_EM_FINDTEXT(hwnd, name, &find[i], i);
176 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
179 check_EM_FINDTEXT(hwnd, name, &find[i], i);
180 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
185 static void test_EM_FINDTEXT(void)
187 HWND hwndRichEdit = new_richedit(NULL);
189 /* Empty rich edit control */
190 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
191 sizeof(find_tests)/sizeof(struct find_s));
193 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
196 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
197 sizeof(find_tests2)/sizeof(struct find_s));
199 DestroyWindow(hwndRichEdit);
202 static const struct getline_s {
207 {0, 10, "foo bar\r"},
212 /* Buffer smaller than line length */
218 static void test_EM_GETLINE(void)
221 HWND hwndRichEdit = new_richedit(NULL);
222 static const int nBuf = 1024;
223 char dest[1024], origdest[1024];
224 const char text[] = "foo bar\n"
228 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
230 memset(origdest, 0xBB, nBuf);
231 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
234 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
235 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
236 memset(dest, 0xBB, nBuf);
237 *(WORD *) dest = gl[i].buffer_len;
239 /* EM_GETLINE appends a "\r\0" to the end of the line
240 * nCopied counts up to and including the '\r' */
241 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
242 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
244 /* two special cases since a parameter is passed via dest */
245 if (gl[i].buffer_len == 0)
246 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
248 else if (gl[i].buffer_len == 1)
249 ok(dest[0] == gl[i].text[0] && !dest[1] &&
250 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
253 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
254 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
255 ok(!strncmp(dest + expected_bytes_written, origdest
256 + expected_bytes_written, nBuf - expected_bytes_written),
257 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
261 DestroyWindow(hwndRichEdit);
264 static int get_scroll_pos_y(HWND hwnd)
267 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
268 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
272 static void move_cursor(HWND hwnd, long charindex)
275 cr.cpMax = charindex;
276 cr.cpMin = charindex;
277 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
280 static void line_scroll(HWND hwnd, int amount)
282 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
285 static void test_EM_SCROLLCARET(void)
288 HWND hwndRichEdit = new_richedit(NULL);
289 const char text[] = "aa\n"
290 "this is a long line of text that should be longer than the "
299 /* Can't verify this */
300 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
302 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
304 /* Caret above visible window */
305 line_scroll(hwndRichEdit, 3);
306 prevY = get_scroll_pos_y(hwndRichEdit);
307 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
308 curY = get_scroll_pos_y(hwndRichEdit);
309 ok(prevY != curY, "%d == %d\n", prevY, curY);
311 /* Caret below visible window */
312 move_cursor(hwndRichEdit, sizeof(text) - 1);
313 line_scroll(hwndRichEdit, -3);
314 prevY = get_scroll_pos_y(hwndRichEdit);
315 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
316 curY = get_scroll_pos_y(hwndRichEdit);
317 ok(prevY != curY, "%d == %d\n", prevY, curY);
319 /* Caret in visible window */
320 move_cursor(hwndRichEdit, sizeof(text) - 2);
321 prevY = get_scroll_pos_y(hwndRichEdit);
322 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
323 curY = get_scroll_pos_y(hwndRichEdit);
324 ok(prevY == curY, "%d != %d\n", prevY, curY);
326 /* Caret still in visible window */
327 line_scroll(hwndRichEdit, -1);
328 prevY = get_scroll_pos_y(hwndRichEdit);
329 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
330 curY = get_scroll_pos_y(hwndRichEdit);
331 ok(prevY == curY, "%d != %d\n", prevY, curY);
333 DestroyWindow(hwndRichEdit);
336 static void test_EM_SETCHARFORMAT(void)
338 HWND hwndRichEdit = new_richedit(NULL);
342 /* Invalid flags, CHARFORMAT2 structure blanked out */
343 memset(&cf2, 0, sizeof(cf2));
344 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
346 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
348 /* A valid flag, CHARFORMAT2 structure blanked out */
349 memset(&cf2, 0, sizeof(cf2));
350 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
352 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
354 /* A valid flag, CHARFORMAT2 structure blanked out */
355 memset(&cf2, 0, sizeof(cf2));
356 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
358 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
360 /* A valid flag, CHARFORMAT2 structure blanked out */
361 memset(&cf2, 0, sizeof(cf2));
362 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
364 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
366 /* A valid flag, CHARFORMAT2 structure blanked out */
367 memset(&cf2, 0, sizeof(cf2));
368 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
370 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
372 /* Invalid flags, CHARFORMAT2 structure minimally filled */
373 memset(&cf2, 0, sizeof(cf2));
374 cf2.cbSize = sizeof(CHARFORMAT2);
375 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
377 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
379 /* A valid flag, CHARFORMAT2 structure minimally filled */
380 memset(&cf2, 0, sizeof(cf2));
381 cf2.cbSize = sizeof(CHARFORMAT2);
382 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
384 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
386 /* A valid flag, CHARFORMAT2 structure minimally filled */
387 memset(&cf2, 0, sizeof(cf2));
388 cf2.cbSize = sizeof(CHARFORMAT2);
389 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
391 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
393 /* A valid flag, CHARFORMAT2 structure minimally filled */
394 memset(&cf2, 0, sizeof(cf2));
395 cf2.cbSize = sizeof(CHARFORMAT2);
396 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
398 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
400 /* A valid flag, CHARFORMAT2 structure minimally filled */
401 memset(&cf2, 0, sizeof(cf2));
402 cf2.cbSize = sizeof(CHARFORMAT2);
403 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
405 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
407 DestroyWindow(hwndRichEdit);
410 static void test_EM_SETTEXTMODE(void)
412 HWND hwndRichEdit = new_richedit(NULL);
413 CHARFORMAT2 cf2, cf2test;
417 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
418 /*Insert text into the control*/
420 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
422 /*Attempt to change the control to plain text mode*/
423 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
424 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
426 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
427 If rich text is pasted, it should have the same formatting as the rest
428 of the text in the control*/
431 *NOTE: If the default text was already italicized, the test will simply
432 reverse; in other words, it will copy a regular "wine" into a plain
433 text window that uses an italicized format*/
434 cf2.cbSize = sizeof(CHARFORMAT2);
435 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
438 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
439 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
441 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
442 however, SCF_ALL has been implemented*/
443 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
444 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
445 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
447 /*Select the string "wine"*/
450 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
452 /*Copy the italicized "wine" to the clipboard*/
453 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
455 /*Reset the formatting to default*/
456 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
457 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
458 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
460 /*Clear the text in the control*/
461 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
463 /*Switch to Plain Text Mode*/
464 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
465 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
467 /*Input "wine" again in normal format*/
468 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
470 /*Paste the italicized "wine" into the control*/
471 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
473 /*Select a character from the first "wine" string*/
476 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
478 /*Retrieve its formatting*/
479 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
482 /*Select a character from the second "wine" string*/
485 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
487 /*Retrieve its formatting*/
488 cf2test.cbSize = sizeof(CHARFORMAT2);
489 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
492 /*Compare the two formattings*/
493 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
494 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
495 cf2.dwEffects, cf2test.dwEffects);
496 /*Test TM_RICHTEXT by: switching back to Rich Text mode
497 printing "wine" in the current format(normal)
498 pasting "wine" from the clipboard(italicized)
499 comparing the two formats(should differ)*/
501 /*Attempt to switch with text in control*/
502 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
503 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
506 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
508 /*Switch into Rich Text mode*/
509 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
510 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
512 /*Print "wine" in normal formatting into the control*/
513 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
515 /*Paste italicized "wine" into the control*/
516 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
518 /*Select text from the first "wine" string*/
521 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
523 /*Retrieve its formatting*/
524 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
527 /*Select text from the second "wine" string*/
530 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
532 /*Retrieve its formatting*/
533 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
536 /*Test that the two formattings are not the same*/
537 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
538 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
539 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
541 DestroyWindow(hwndRichEdit);
544 static void test_TM_PLAINTEXT(void)
546 /*Tests plain text properties*/
548 HWND hwndRichEdit = new_richedit(NULL);
549 CHARFORMAT2 cf2, cf2test;
553 /*Switch to plain text mode*/
555 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
556 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
558 /*Fill control with text*/
560 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
562 /*Select some text and bold it*/
566 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
567 cf2.cbSize = sizeof(CHARFORMAT2);
568 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
571 cf2.dwMask = CFM_BOLD | cf2.dwMask;
572 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
574 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
575 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
577 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
578 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
580 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
581 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
583 /*Get the formatting of those characters*/
585 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
587 /*Get the formatting of some other characters*/
588 cf2test.cbSize = sizeof(CHARFORMAT2);
591 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
592 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
594 /*Test that they are the same as plain text allows only one formatting*/
596 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
597 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
598 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
600 /*Fill the control with a "wine" string, which when inserted will be bold*/
602 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
604 /*Copy the bolded "wine" string*/
608 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
609 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
611 /*Swap back to rich text*/
613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
614 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
616 /*Set the default formatting to bold italics*/
618 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
619 cf2.dwMask |= CFM_ITALIC;
620 cf2.dwEffects ^= CFE_ITALIC;
621 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
622 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
624 /*Set the text in the control to "wine", which will be bold and italicized*/
626 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
628 /*Paste the plain text "wine" string, which should take the insert
629 formatting, which at the moment is bold italics*/
631 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
633 /*Select the first "wine" string and retrieve its formatting*/
637 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
638 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
640 /*Select the second "wine" string and retrieve its formatting*/
644 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
645 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
647 /*Compare the two formattings. They should be the same.*/
649 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
650 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
651 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
652 DestroyWindow(hwndRichEdit);
655 static void test_WM_GETTEXT(void)
657 HWND hwndRichEdit = new_richedit(NULL);
658 static const char text[] = "Hello. My name is RichEdit!";
659 static const char text2[] = "Hello. My name is RichEdit!\r";
660 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
661 char buffer[1024] = {0};
664 /* Baseline test with normal-sized buffer */
665 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
666 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
667 ok(result == lstrlen(buffer),
668 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
669 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
670 result = strcmp(buffer,text);
672 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
674 /* Test for returned value of WM_GETTEXTLENGTH */
675 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
676 ok(result == lstrlen(text),
677 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
678 result, lstrlen(text));
680 /* Test for behavior in overflow case */
681 memset(buffer, 0, 1024);
682 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
684 result == lstrlenA(text) - 1, /* XP, win2k3 */
685 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
686 result = strcmp(buffer,text);
688 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
690 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
692 /* Baseline test with normal-sized buffer and carriage return */
693 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
694 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
695 ok(result == lstrlen(buffer),
696 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
697 result = strcmp(buffer,text2_after);
699 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
701 /* Test for returned value of WM_GETTEXTLENGTH */
702 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
703 ok(result == lstrlen(text2_after),
704 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
705 result, lstrlen(text2_after));
707 /* Test for behavior of CRLF conversion in case of overflow */
708 memset(buffer, 0, 1024);
709 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
711 result == lstrlenA(text2) - 1, /* XP, win2k3 */
712 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
713 result = strcmp(buffer,text2);
715 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
717 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
719 DestroyWindow(hwndRichEdit);
722 /* FIXME: need to test unimplemented options and robustly test wparam */
723 static void test_EM_SETOPTIONS(void)
725 HWND hwndRichEdit = new_richedit(NULL);
726 static const char text[] = "Hello. My name is RichEdit!";
727 char buffer[1024] = {0};
729 /* NEGATIVE TESTING - NO OPTIONS SET */
730 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
731 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
733 /* testing no readonly by sending 'a' to the control*/
734 SetFocus(hwndRichEdit);
735 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
736 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
738 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
741 /* READONLY - sending 'a' to the control */
742 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
743 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
744 SetFocus(hwndRichEdit);
745 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
746 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
747 ok(buffer[0]==text[0],
748 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
750 DestroyWindow(hwndRichEdit);
753 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
755 CHARFORMAT2W text_format;
756 int link_present = 0;
757 text_format.cbSize = sizeof(text_format);
758 SendMessage(hwnd, EM_SETSEL, 0, 1);
759 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
760 link_present = text_format.dwEffects & CFE_LINK;
762 { /* control text is url; should get CFE_LINK */
763 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
767 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
771 static HWND new_static_wnd(HWND parent) {
772 return new_window("Static", 0, parent);
775 static void test_EM_AUTOURLDETECT(void)
782 {"http://www.winehq.org", 1},
783 {"http//winehq.org", 0},
784 {"ww.winehq.org", 0},
785 {"www.winehq.org", 1},
786 {"ftp://192.168.1.1", 1},
787 {"ftp//192.168.1.1", 0},
788 {"mailto:your@email.com", 1},
789 {"prospero:prosperoserver", 1},
791 {"news:newserver", 1},
792 {"wais:waisserver", 1}
797 HWND hwndRichEdit, parent;
799 parent = new_static_wnd(NULL);
800 hwndRichEdit = new_richedit(parent);
801 /* Try and pass EM_AUTOURLDETECT some test wParam values */
802 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
803 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
804 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
805 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
806 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
807 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
808 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
809 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
810 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
811 /* for each url, check the text to see if CFE_LINK effect is present */
812 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
813 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
814 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
815 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
816 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
817 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
818 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
819 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
820 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
822 DestroyWindow(hwndRichEdit);
823 DestroyWindow(parent);
826 static void test_EM_SCROLL(void)
829 int r; /* return value */
830 int expr; /* expected return value */
831 HWND hwndRichEdit = new_richedit(NULL);
832 int y_before, y_after; /* units of lines of text */
834 /* test a richedit box containing a single line of text */
835 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
837 for (i = 0; i < 4; i++) {
838 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
840 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
841 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
842 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
843 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
844 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
845 "(i == %d)\n", y_after, i);
849 * test a richedit box that will scroll. There are two general
850 * cases: the case without any long lines and the case with a long
853 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
855 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
857 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
858 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
859 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
860 "LONG LINE \nb\nc\nd\ne");
861 for (j = 0; j < 12; j++) /* reset scroll position to top */
862 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
864 /* get first visible line */
865 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
866 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
868 /* get new current first visible line */
869 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
871 ok(((r & 0xffffff00) == 0x00010000) &&
872 ((r & 0x000000ff) != 0x00000000),
873 "EM_SCROLL page down didn't scroll by a small positive number of "
874 "lines (r == 0x%08x)\n", r);
875 ok(y_after > y_before, "EM_SCROLL page down not functioning "
876 "(line %d scrolled to line %d\n", y_before, y_after);
880 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
881 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
882 ok(((r & 0xffffff00) == 0x0001ff00),
883 "EM_SCROLL page up didn't scroll by a small negative number of lines "
884 "(r == 0x%08x)\n", r);
885 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
886 "%d scrolled to line %d\n", y_before, y_after);
890 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
892 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
894 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
895 "(r == 0x%08x)\n", r);
896 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
897 "1 line (%d scrolled to %d)\n", y_before, y_after);
901 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
903 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
905 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
906 "(r == 0x%08x)\n", r);
907 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
908 "line (%d scrolled to %d)\n", y_before, y_after);
912 r = SendMessage(hwndRichEdit, EM_SCROLL,
913 SB_LINEUP, 0); /* lineup beyond top */
915 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
918 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
919 ok(y_before == y_after,
920 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
924 r = SendMessage(hwndRichEdit, EM_SCROLL,
925 SB_PAGEUP, 0);/*page up beyond top */
927 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
930 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
931 ok(y_before == y_after,
932 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
934 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
935 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
936 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
937 r = SendMessage(hwndRichEdit, EM_SCROLL,
938 SB_PAGEDOWN, 0); /* page down beyond bot */
939 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
942 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
943 ok(y_before == y_after,
944 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
947 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
948 SendMessage(hwndRichEdit, EM_SCROLL,
949 SB_LINEDOWN, 0); /* line down beyond bot */
950 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
953 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
954 ok(y_before == y_after,
955 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
958 DestroyWindow(hwndRichEdit);
961 static void test_EM_SETUNDOLIMIT(void)
963 /* cases we test for:
964 * default behaviour - limiting at 100 undo's
965 * undo disabled - setting a limit of 0
966 * undo limited - undo limit set to some to some number, like 2
967 * bad input - sending a negative number should default to 100 undo's */
969 HWND hwndRichEdit = new_richedit(NULL);
974 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
977 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
978 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
979 also, multiple pastes don't combine like WM_CHAR would */
980 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
982 /* first case - check the default */
983 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
984 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
985 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
986 for (i=0; i<100; i++) /* Undo 100 of them */
987 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
988 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
989 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
991 /* second case - cannot undo */
992 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
993 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
994 SendMessage(hwndRichEdit,
995 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
996 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
997 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
999 /* third case - set it to an arbitrary number */
1000 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
1001 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
1002 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1003 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1004 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1005 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
1006 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
1007 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
1008 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1009 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1010 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
1011 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1012 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1013 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
1015 /* fourth case - setting negative numbers should default to 100 undos */
1016 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1017 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
1019 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
1021 DestroyWindow(hwndRichEdit);
1024 static void test_ES_PASSWORD(void)
1026 /* This isn't hugely testable, so we're just going to run it through its paces */
1028 HWND hwndRichEdit = new_richedit(NULL);
1031 /* First, check the default of a regular control */
1032 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1034 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
1036 /* Now, set it to something normal */
1037 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
1038 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1040 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1042 /* Now, set it to something odd */
1043 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
1044 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1046 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1047 DestroyWindow(hwndRichEdit);
1050 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
1055 char** str = (char**)dwCookie;
1058 memcpy(*str, pbBuff, *pcb);
1064 static void test_WM_SETTEXT()
1066 HWND hwndRichEdit = new_richedit(NULL);
1067 const char * TestItem1 = "TestSomeText";
1068 const char * TestItem2 = "TestSomeText\r";
1069 const char * TestItem2_after = "TestSomeText\r\n";
1070 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
1071 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
1072 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
1073 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
1074 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
1075 const char * TestItem5_after = "TestSomeText TestSomeText";
1076 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
1077 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
1078 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
1079 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
1081 char buf[1024] = {0};
1086 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1087 any solitary \r to be converted to \r\n on return. Properly paired
1088 \r\n are not affected. It also shows that the special sequence \r\r\n
1089 gets converted to a single space.
1092 #define TEST_SETTEXT(a, b) \
1093 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
1094 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
1095 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
1096 ok (result == lstrlen(buf), \
1097 "WM_GETTEXT returned %ld instead of expected %u\n", \
1098 result, lstrlen(buf)); \
1099 result = strcmp(b, buf); \
1101 "WM_SETTEXT round trip: strcmp = %ld\n", result);
1103 TEST_SETTEXT(TestItem1, TestItem1)
1104 TEST_SETTEXT(TestItem2, TestItem2_after)
1105 TEST_SETTEXT(TestItem3, TestItem3_after)
1106 TEST_SETTEXT(TestItem3_after, TestItem3_after)
1107 TEST_SETTEXT(TestItem4, TestItem4_after)
1108 TEST_SETTEXT(TestItem5, TestItem5_after)
1109 TEST_SETTEXT(TestItem6, TestItem6_after)
1110 TEST_SETTEXT(TestItem7, TestItem7_after)
1112 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
1113 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1115 es.dwCookie = (DWORD_PTR)&p;
1117 es.pfnCallback = test_WM_SETTEXT_esCallback;
1118 memset(buf, 0, sizeof(buf));
1119 SendMessage(hwndRichEdit, EM_STREAMOUT,
1120 (WPARAM)(SF_RTF), (LPARAM)&es);
1121 trace("EM_STREAMOUT produced: \n%s\n", buf);
1122 TEST_SETTEXT(buf, TestItem1)
1125 DestroyWindow(hwndRichEdit);
1128 static void test_EM_SETTEXTEX(void)
1130 HWND hwndRichEdit = new_richedit(NULL);
1133 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1135 'T', 'e', 'x', 't', 0};
1136 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1140 const char * TestItem2_after = "TestSomeText\r\n";
1141 WCHAR TestItem3[] = {'T', 'e', 's', 't',
1144 '\r','\n','\r','\n', 0};
1145 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
1149 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
1153 WCHAR TestItem4[] = {'T', 'e', 's', 't',
1156 '\r','\r','\n','\r',
1158 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
1162 #define MAX_BUF_LEN 1024
1163 WCHAR buf[MAX_BUF_LEN];
1169 setText.codepage = 1200; /* no constant for unicode */
1170 getText.codepage = 1200; /* no constant for unicode */
1171 getText.cb = MAX_BUF_LEN;
1172 getText.flags = GT_DEFAULT;
1173 getText.lpDefaultChar = NULL;
1174 getText.lpUsedDefChar = NULL;
1177 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1178 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1179 ok(lstrcmpW(buf, TestItem1) == 0,
1180 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1182 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1183 convert \r to \r\n on return
1185 setText.codepage = 1200; /* no constant for unicode */
1186 getText.codepage = 1200; /* no constant for unicode */
1187 getText.cb = MAX_BUF_LEN;
1188 getText.flags = GT_DEFAULT;
1189 getText.lpDefaultChar = NULL;
1190 getText.lpUsedDefChar = NULL;
1192 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1193 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1194 ok(lstrcmpW(buf, TestItem2) == 0,
1195 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1197 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1198 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1199 ok(strcmp((const char *)buf, TestItem2_after) == 0,
1200 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1202 /* Baseline test for just-enough buffer space for string */
1203 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1204 getText.codepage = 1200; /* no constant for unicode */
1205 getText.flags = GT_DEFAULT;
1206 getText.lpDefaultChar = NULL;
1207 getText.lpUsedDefChar = NULL;
1208 memset(buf, 0, MAX_BUF_LEN);
1209 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1210 ok(lstrcmpW(buf, TestItem2) == 0,
1211 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1213 /* When there is enough space for one character, but not both, of the CRLF
1214 pair at the end of the string, the CR is not copied at all. That is,
1215 the caller must not see CRLF pairs truncated to CR at the end of the
1218 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1219 getText.codepage = 1200; /* no constant for unicode */
1220 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
1221 getText.lpDefaultChar = NULL;
1222 getText.lpUsedDefChar = NULL;
1223 memset(buf, 0, MAX_BUF_LEN);
1224 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1225 ok(lstrcmpW(buf, TestItem1) == 0,
1226 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1229 /* \r\n pairs get changed into \r */
1230 setText.codepage = 1200; /* no constant for unicode */
1231 getText.codepage = 1200; /* no constant for unicode */
1232 getText.cb = MAX_BUF_LEN;
1233 getText.flags = GT_DEFAULT;
1234 getText.lpDefaultChar = NULL;
1235 getText.lpUsedDefChar = NULL;
1237 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
1238 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1239 ok(lstrcmpW(buf, TestItem3_after) == 0,
1240 "EM_SETTEXTEX did not convert properly\n");
1242 /* \n also gets changed to \r */
1243 setText.codepage = 1200; /* no constant for unicode */
1244 getText.codepage = 1200; /* no constant for unicode */
1245 getText.cb = MAX_BUF_LEN;
1246 getText.flags = GT_DEFAULT;
1247 getText.lpDefaultChar = NULL;
1248 getText.lpUsedDefChar = NULL;
1250 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
1251 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1252 ok(lstrcmpW(buf, TestItem3_after) == 0,
1253 "EM_SETTEXTEX did not convert properly\n");
1255 /* \r\r\n gets changed into single space */
1256 setText.codepage = 1200; /* no constant for unicode */
1257 getText.codepage = 1200; /* no constant for unicode */
1258 getText.cb = MAX_BUF_LEN;
1259 getText.flags = GT_DEFAULT;
1260 getText.lpDefaultChar = NULL;
1261 getText.lpUsedDefChar = NULL;
1263 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
1264 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1265 ok(lstrcmpW(buf, TestItem4_after) == 0,
1266 "EM_SETTEXTEX did not convert properly\n");
1268 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1269 (WPARAM)&setText, (LPARAM) NULL);
1270 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1273 "EM_SETTEXTEX returned %d, instead of 1\n",result);
1274 ok(lstrlenW(buf) == 0,
1275 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1277 /* put some text back */
1279 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1280 /* select some text */
1283 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1284 /* replace current selection */
1285 setText.flags = ST_SELECTION;
1286 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1287 (WPARAM)&setText, (LPARAM) NULL);
1289 "EM_SETTEXTEX with NULL lParam to replace selection"
1290 " with no text should return 0. Got %i\n",
1293 /* put some text back */
1295 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1296 /* select some text */
1299 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1300 /* replace current selection */
1301 setText.flags = ST_SELECTION;
1302 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1303 (WPARAM)&setText, (LPARAM) TestItem1);
1305 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1306 ok(result == lstrlenW(TestItem1),
1307 "EM_SETTEXTEX with NULL lParam to replace selection"
1308 " with no text should return 0. Got %i\n",
1310 ok(lstrlenW(buf) == 22,
1311 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1314 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
1315 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
1317 es.dwCookie = (DWORD_PTR)&p;
1319 es.pfnCallback = test_WM_SETTEXT_esCallback;
1320 memset(buf, 0, sizeof(buf));
1321 SendMessage(hwndRichEdit, EM_STREAMOUT,
1322 (WPARAM)(SF_RTF), (LPARAM)&es);
1323 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
1325 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
1326 getText.codepage = 1200; /* no constant for unicode */
1327 getText.cb = MAX_BUF_LEN;
1328 getText.flags = GT_DEFAULT;
1329 getText.lpDefaultChar = NULL;
1330 getText.lpUsedDefChar = NULL;
1333 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
1334 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1335 ok(lstrcmpW(buf, TestItem1) == 0,
1336 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1339 DestroyWindow(hwndRichEdit);
1342 static void test_EM_LIMITTEXT(void)
1346 HWND hwndRichEdit = new_richedit(NULL);
1348 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1349 * about setting the length to -1 for multiline edit controls doesn't happen.
1352 /* Don't check default gettextlimit case. That's done in other tests */
1354 /* Set textlimit to 100 */
1355 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1356 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1358 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1360 /* Set textlimit to 0 */
1361 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1362 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1364 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1366 /* Set textlimit to -1 */
1367 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1368 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1370 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1372 /* Set textlimit to -2 */
1373 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1374 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1376 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1378 DestroyWindow (hwndRichEdit);
1382 static void test_EM_EXLIMITTEXT(void)
1384 int i, selBegin, selEnd, len1, len2;
1386 char text[1024 + 1];
1387 char buffer[1024 + 1];
1388 int textlimit = 0; /* multiple of 100 */
1389 HWND hwndRichEdit = new_richedit(NULL);
1391 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1392 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1395 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1396 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1398 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1401 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1402 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1404 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1406 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1407 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1408 /* default for WParam = 0 */
1409 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1411 textlimit = sizeof(text)-1;
1412 memset(text, 'W', textlimit);
1413 text[sizeof(text)-1] = 0;
1414 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1415 /* maxed out text */
1416 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1418 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1419 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1420 len1 = selEnd - selBegin;
1422 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1423 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1424 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1425 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1426 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1427 len2 = selEnd - selBegin;
1430 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1433 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1434 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1435 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1436 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1437 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1438 len1 = selEnd - selBegin;
1441 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1444 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1445 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1446 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1447 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1448 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1449 len2 = selEnd - selBegin;
1452 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1455 /* set text up to the limit, select all the text, then add a char */
1457 memset(text, 'W', textlimit);
1458 text[textlimit] = 0;
1459 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1460 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1461 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1462 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1463 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1464 result = strcmp(buffer, "A");
1465 ok(0 == result, "got string = \"%s\"\n", buffer);
1467 /* WM_SETTEXT not limited */
1469 memset(text, 'W', textlimit);
1470 text[textlimit] = 0;
1471 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1472 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1473 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1475 ok(10 == i, "expected 10 chars\n");
1476 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1477 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1479 /* try inserting more text at end */
1480 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1481 ok(0 == i, "WM_CHAR wasn't processed\n");
1482 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1484 ok(10 == i, "expected 10 chars, got %i\n", i);
1485 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1486 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1488 /* try inserting text at beginning */
1489 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1490 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1491 ok(0 == i, "WM_CHAR wasn't processed\n");
1492 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1494 ok(10 == i, "expected 10 chars, got %i\n", i);
1495 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1496 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1498 /* WM_CHAR is limited */
1500 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1501 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1502 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1503 ok(0 == i, "WM_CHAR wasn't processed\n");
1504 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1505 ok(0 == i, "WM_CHAR wasn't processed\n");
1506 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1508 ok(1 == i, "expected 1 chars, got %i instead\n", i);
1510 DestroyWindow(hwndRichEdit);
1513 static void test_EM_GETLIMITTEXT(void)
1516 HWND hwndRichEdit = new_richedit(NULL);
1518 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1519 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1521 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1522 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1523 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1525 DestroyWindow(hwndRichEdit);
1528 static void test_WM_SETFONT(void)
1530 /* There is no invalid input or error conditions for this function.
1531 * NULL wParam and lParam just fall back to their default values
1532 * It should be noted that even if you use a gibberish name for your fonts
1533 * here, it will still work because the name is stored. They will display as
1534 * System, but will report their name to be whatever they were created as */
1536 HWND hwndRichEdit = new_richedit(NULL);
1537 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1538 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1539 FF_DONTCARE, "Marlett");
1540 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1541 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1542 FF_DONTCARE, "MS Sans Serif");
1543 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1544 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1545 FF_DONTCARE, "Courier");
1546 LOGFONTA sentLogFont;
1547 CHARFORMAT2A returnedCF2A;
1549 returnedCF2A.cbSize = sizeof(returnedCF2A);
1551 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1552 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1553 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1555 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1556 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1557 "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1558 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1560 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1561 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1562 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1563 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1564 "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1565 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1567 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1568 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1569 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1570 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1571 "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1572 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1574 /* This last test is special since we send in NULL. We clear the variables
1575 * and just compare to "System" instead of the sent in font name. */
1576 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1577 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1578 returnedCF2A.cbSize = sizeof(returnedCF2A);
1580 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1581 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1582 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1583 ok (!strcmp("System",returnedCF2A.szFaceName),
1584 "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1586 DestroyWindow(hwndRichEdit);
1590 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1595 const char** str = (const char**)dwCookie;
1596 int size = strlen(*str);
1597 if(size > 3) /* let's make it piecemeal for fun */
1604 memcpy(pbBuff, *str, *pcb);
1610 static void test_EM_GETMODIFY(void)
1612 HWND hwndRichEdit = new_richedit(NULL);
1615 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1617 'T', 'e', 'x', 't', 0};
1618 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1620 'O', 't', 'h', 'e', 'r',
1621 'T', 'e', 'x', 't', 0};
1622 const char* streamText = "hello world";
1627 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1628 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1629 FF_DONTCARE, "Courier");
1631 setText.codepage = 1200; /* no constant for unicode */
1632 setText.flags = ST_KEEPUNDO;
1635 /* modify flag shouldn't be set when richedit is first created */
1636 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1638 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1640 /* setting modify flag should actually set it */
1641 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1642 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1644 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1646 /* clearing modify flag should actually clear it */
1647 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1648 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1650 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1652 /* setting font doesn't change modify flag */
1653 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1654 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1655 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1657 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1659 /* setting text should set modify flag */
1660 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1661 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1662 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1664 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1666 /* undo previous text doesn't reset modify flag */
1667 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1668 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1670 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1672 /* set text with no flag to keep undo stack should not set modify flag */
1673 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1675 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1676 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1678 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1680 /* WM_SETTEXT doesn't modify */
1681 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1682 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1683 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1685 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1687 /* clear the text */
1688 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1689 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1690 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1692 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1695 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1696 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1697 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1698 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1699 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1701 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1703 /* copy/paste text 1 */
1704 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1705 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1706 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1707 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1708 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1710 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1712 /* copy/paste text 2 */
1713 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1714 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1715 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1716 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1717 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1718 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1720 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1723 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1724 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1725 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1726 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1728 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1731 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1732 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1733 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1734 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1736 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1738 /* set char format */
1739 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1740 cf2.cbSize = sizeof(CHARFORMAT2);
1741 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1743 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1744 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1745 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1746 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1747 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
1748 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1750 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1752 /* set para format */
1753 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1754 pf2.cbSize = sizeof(PARAFORMAT2);
1755 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1757 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1758 pf2.wAlignment = PFA_RIGHT;
1759 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1760 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1762 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1765 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1766 es.dwCookie = (DWORD_PTR)&streamText;
1768 es.pfnCallback = test_EM_GETMODIFY_esCallback;
1769 SendMessage(hwndRichEdit, EM_STREAMIN,
1770 (WPARAM)(SF_TEXT), (LPARAM)&es);
1771 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1773 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1775 DestroyWindow(hwndRichEdit);
1781 long expected_retval;
1782 int expected_getsel_start;
1783 int expected_getsel_end;
1784 int _exsetsel_todo_wine;
1785 int _getsel_todo_wine;
1788 const struct exsetsel_s exsetsel_tests[] = {
1790 {5, 10, 10, 5, 10, 0, 0},
1791 {15, 17, 17, 15, 17, 0, 0},
1792 /* test cpMax > strlen() */
1793 {0, 100, 18, 0, 18, 0, 1},
1794 /* test cpMin == cpMax */
1795 {5, 5, 5, 5, 5, 0, 0},
1796 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1797 {-1, 0, 5, 5, 5, 0, 0},
1798 {-1, 17, 5, 5, 5, 0, 0},
1799 {-1, 18, 5, 5, 5, 0, 0},
1800 /* test cpMin < 0 && cpMax < 0 */
1801 {-1, -1, 17, 17, 17, 0, 0},
1802 {-4, -5, 17, 17, 17, 0, 0},
1803 /* test cMin >=0 && cpMax < 0 (bug 6814) */
1804 {0, -1, 18, 0, 18, 0, 1},
1805 {17, -5, 18, 17, 18, 0, 1},
1806 {18, -3, 17, 17, 17, 0, 0},
1807 /* test if cpMin > cpMax */
1808 {15, 19, 18, 15, 18, 0, 1},
1809 {19, 15, 18, 15, 18, 0, 1}
1812 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1817 cr.cpMin = setsel->min;
1818 cr.cpMax = setsel->max;
1819 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1821 if (setsel->_exsetsel_todo_wine) {
1823 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1826 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1829 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1831 if (setsel->_getsel_todo_wine) {
1833 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);
1836 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);
1840 static void test_EM_EXSETSEL(void)
1842 HWND hwndRichEdit = new_richedit(NULL);
1844 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1846 /* sending some text to the window */
1847 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1848 /* 01234567890123456*/
1851 for (i = 0; i < num_tests; i++) {
1852 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1855 DestroyWindow(hwndRichEdit);
1858 static void test_EM_REPLACESEL(int redraw)
1860 HWND hwndRichEdit = new_richedit(NULL);
1861 char buffer[1024] = {0};
1866 /* sending some text to the window */
1867 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1868 /* 01234567890123456*/
1871 /* FIXME add more tests */
1872 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
1873 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
1874 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
1875 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1876 r = strcmp(buffer, "testing");
1877 ok(0 == r, "expected %d, got %d\n", 0, r);
1879 DestroyWindow(hwndRichEdit);
1881 hwndRichEdit = new_richedit(NULL);
1883 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
1884 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
1886 /* Test behavior with carriage returns and newlines */
1887 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1888 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
1889 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
1890 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1891 r = strcmp(buffer, "RichEdit1");
1892 ok(0 == r, "expected %d, got %d\n", 0, r);
1894 getText.codepage = CP_ACP;
1895 getText.flags = GT_DEFAULT;
1896 getText.lpDefaultChar = NULL;
1897 getText.lpUsedDefChar = NULL;
1898 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1899 ok(strcmp(buffer, "RichEdit1") == 0,
1900 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
1902 /* Test number of lines reported after EM_REPLACESEL */
1903 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
1904 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
1906 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1907 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
1908 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
1909 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1910 r = strcmp(buffer, "RichEdit1\r\n");
1911 ok(0 == r, "expected %d, got %d\n", 0, r);
1913 getText.codepage = CP_ACP;
1914 getText.flags = GT_DEFAULT;
1915 getText.lpDefaultChar = NULL;
1916 getText.lpUsedDefChar = NULL;
1917 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1918 ok(strcmp(buffer, "RichEdit1\r") == 0,
1919 "EM_GETTEXTEX returned incorrect string\n");
1921 /* Test number of lines reported after EM_REPLACESEL */
1922 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
1923 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
1925 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
1926 EM_REPLACESEL. The general rule seems to be that Win98's riched20
1927 returns the number of characters *inserted* into the control (after
1928 required conversions), but WinXP's riched20 returns the number of
1929 characters interpreted from the original lParam. Wine's builtin riched20
1930 implements the WinXP behavior.
1932 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1933 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
1934 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
1935 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
1937 /* Test number of lines reported after EM_REPLACESEL */
1938 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
1939 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
1941 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1942 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1943 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
1944 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
1946 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1947 r = strcmp(buffer, "RichEdit1\r\n");
1948 ok(0 == r, "expected %d, got %d\n", 0, r);
1950 getText.codepage = CP_ACP;
1951 getText.flags = GT_DEFAULT;
1952 getText.lpDefaultChar = NULL;
1953 getText.lpUsedDefChar = NULL;
1954 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1955 ok(strcmp(buffer, "RichEdit1\r") == 0,
1956 "EM_GETTEXTEX returned incorrect string\n");
1958 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1959 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1960 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
1961 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
1963 /* The following tests show that richedit should handle the special \r\r\n
1964 sequence by turning it into a single space on insertion. However,
1965 EM_REPLACESEL on WinXP returns the number of characters in the original
1969 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1970 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
1971 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
1972 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1973 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1974 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
1975 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
1977 /* Test the actual string */
1979 getText.codepage = CP_ACP;
1980 getText.flags = GT_DEFAULT;
1981 getText.lpDefaultChar = NULL;
1982 getText.lpUsedDefChar = NULL;
1983 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1984 ok(strcmp(buffer, "\r\r") == 0,
1985 "EM_GETTEXTEX returned incorrect string\n");
1987 /* Test number of lines reported after EM_REPLACESEL */
1988 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
1989 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
1991 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1992 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
1993 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
1994 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
1995 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1996 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1997 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
1998 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
2000 /* Test the actual string */
2002 getText.codepage = CP_ACP;
2003 getText.flags = GT_DEFAULT;
2004 getText.lpDefaultChar = NULL;
2005 getText.lpUsedDefChar = NULL;
2006 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2007 ok(strcmp(buffer, " ") == 0,
2008 "EM_GETTEXTEX returned incorrect string\n");
2010 /* Test number of lines reported after EM_REPLACESEL */
2011 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2012 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2014 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2015 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
2016 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2017 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2018 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2019 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2020 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2021 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2023 /* Test the actual string */
2025 getText.codepage = CP_ACP;
2026 getText.flags = GT_DEFAULT;
2027 getText.lpDefaultChar = NULL;
2028 getText.lpUsedDefChar = NULL;
2029 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2030 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
2031 "EM_GETTEXTEX returned incorrect string\n");
2033 /* Test number of lines reported after EM_REPLACESEL */
2034 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2035 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2037 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2038 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
2039 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
2040 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
2041 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2042 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2043 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2044 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2046 /* Test the actual string */
2048 getText.codepage = CP_ACP;
2049 getText.flags = GT_DEFAULT;
2050 getText.lpDefaultChar = NULL;
2051 getText.lpUsedDefChar = NULL;
2052 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2053 ok(strcmp(buffer, " \r") == 0,
2054 "EM_GETTEXTEX returned incorrect string\n");
2056 /* Test number of lines reported after EM_REPLACESEL */
2057 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2058 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2060 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2061 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
2062 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
2063 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
2064 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2065 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2066 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
2067 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
2069 /* Test the actual string */
2071 getText.codepage = CP_ACP;
2072 getText.flags = GT_DEFAULT;
2073 getText.lpDefaultChar = NULL;
2074 getText.lpUsedDefChar = NULL;
2075 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2076 ok(strcmp(buffer, " \r\r") == 0,
2077 "EM_GETTEXTEX returned incorrect string\n");
2079 /* Test number of lines reported after EM_REPLACESEL */
2080 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2081 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2083 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2084 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
2085 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
2086 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
2087 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2088 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2089 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
2090 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
2092 /* Test the actual string */
2094 getText.codepage = CP_ACP;
2095 getText.flags = GT_DEFAULT;
2096 getText.lpDefaultChar = NULL;
2097 getText.lpUsedDefChar = NULL;
2098 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2099 ok(strcmp(buffer, "\rX\r\r\r") == 0,
2100 "EM_GETTEXTEX returned incorrect string\n");
2102 /* Test number of lines reported after EM_REPLACESEL */
2103 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2104 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
2106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2107 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
2108 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
2109 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2110 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2111 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2112 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2114 /* Test the actual string */
2116 getText.codepage = CP_ACP;
2117 getText.flags = GT_DEFAULT;
2118 getText.lpDefaultChar = NULL;
2119 getText.lpUsedDefChar = NULL;
2120 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2121 ok(strcmp(buffer, "\r\r") == 0,
2122 "EM_GETTEXTEX returned incorrect string\n");
2124 /* Test number of lines reported after EM_REPLACESEL */
2125 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2126 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2128 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2129 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
2130 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2131 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2132 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2133 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2134 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2135 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2137 /* Test the actual string */
2139 getText.codepage = CP_ACP;
2140 getText.flags = GT_DEFAULT;
2141 getText.lpDefaultChar = NULL;
2142 getText.lpUsedDefChar = NULL;
2143 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2144 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
2145 "EM_GETTEXTEX returned incorrect string\n");
2147 /* Test number of lines reported after EM_REPLACESEL */
2148 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2149 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2151 DestroyWindow(hwndRichEdit);
2154 static void test_WM_PASTE(void)
2158 char buffer[1024] = {0};
2159 char key_info[][3] =
2161 /* VirtualKey, ScanCode, WM_CHAR code */
2162 {'C', 0x2e, 3}, /* Ctrl-C */
2163 {'X', 0x2d, 24}, /* Ctrl-X */
2164 {'V', 0x2f, 22}, /* Ctrl-V */
2165 {'Z', 0x2c, 26}, /* Ctrl-Z */
2166 {'Y', 0x15, 25}, /* Ctrl-Y */
2168 const char* text1 = "testing paste\r";
2169 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
2170 const char* text1_after = "testing paste\r\n";
2171 const char* text2 = "testing paste\r\rtesting paste";
2172 const char* text2_after = "testing paste\r\n\r\ntesting paste";
2173 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
2174 HWND hwndRichEdit = new_richedit(NULL);
2176 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
2177 messages, probably because it inspects the keyboard state itself.
2178 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
2180 #define SEND_CTRL_KEY(hwnd, k) \
2181 keybd_event(VK_CONTROL, 0x1d, 0, 0);\
2182 keybd_event(k[0], k[1], 0, 0);\
2183 keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
2184 keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
2185 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
2186 TranslateMessage(&msg); \
2187 DispatchMessage(&msg); \
2190 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
2191 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
2192 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
2193 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
2194 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
2196 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
2197 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
2199 SEND_CTRL_C(hwndRichEdit) /* Copy */
2200 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2201 SEND_CTRL_V(hwndRichEdit) /* Paste */
2202 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2203 /* Pasted text should be visible at this step */
2204 result = strcmp(text1_step1, buffer);
2206 "test paste: strcmp = %i\n", result);
2207 SEND_CTRL_Z(hwndRichEdit) /* Undo */
2208 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2209 /* Text should be the same as before (except for \r -> \r\n conversion) */
2210 result = strcmp(text1_after, buffer);
2212 "test paste: strcmp = %i\n", result);
2214 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
2215 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
2216 SEND_CTRL_C(hwndRichEdit) /* Copy */
2217 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2218 SEND_CTRL_V(hwndRichEdit) /* Paste */
2219 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2220 /* Pasted text should be visible at this step */
2221 result = strcmp(text3, buffer);
2223 "test paste: strcmp = %i\n", result);
2224 SEND_CTRL_Z(hwndRichEdit) /* Undo */
2225 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2226 /* Text should be the same as before (except for \r -> \r\n conversion) */
2227 result = strcmp(text2_after, buffer);
2229 "test paste: strcmp = %i\n", result);
2230 SEND_CTRL_Y(hwndRichEdit) /* Redo */
2231 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2232 /* Text should revert to post-paste state */
2233 result = strcmp(buffer,text3);
2235 "test paste: strcmp = %i\n", result);
2237 DestroyWindow(hwndRichEdit);
2240 static void test_EM_FORMATRANGE(void)
2245 HWND hwndRichEdit = new_richedit(NULL);
2247 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
2249 hdc = GetDC(hwndRichEdit);
2250 ok(hdc != NULL, "Could not get HDC\n");
2252 fr.hdc = fr.hdcTarget = hdc;
2253 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
2254 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
2255 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
2259 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2261 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2264 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2266 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
2272 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2274 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
2277 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2279 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2282 DestroyWindow(hwndRichEdit);
2285 static int nCallbackCount = 0;
2287 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
2290 const char text[] = {'t','e','s','t'};
2292 if (sizeof(text) <= cb)
2294 if ((int)dwCookie != nCallbackCount)
2300 memcpy (pbBuff, text, sizeof(text));
2301 *pcb = sizeof(text);
2308 return 1; /* indicates callback failed */
2311 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
2316 const char** str = (const char**)dwCookie;
2317 int size = strlen(*str);
2323 memcpy(pbBuff, *str, *pcb);
2330 static void test_EM_STREAMIN(void)
2332 HWND hwndRichEdit = new_richedit(NULL);
2335 char buffer[1024] = {0};
2337 const char * streamText0 = "{\\rtf1 TestSomeText}";
2338 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
2339 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
2341 const char * streamText1 =
2342 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
2343 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
2346 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
2347 const char * streamText2 =
2348 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
2349 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
2350 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
2351 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
2352 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
2353 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
2354 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
2356 const char * streamText3 = "RichEdit1";
2358 /* Minimal test without \par at the end */
2359 es.dwCookie = (DWORD_PTR)&streamText0;
2361 es.pfnCallback = test_EM_STREAMIN_esCallback;
2362 SendMessage(hwndRichEdit, EM_STREAMIN,
2363 (WPARAM)(SF_RTF), (LPARAM)&es);
2365 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2367 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
2368 result = strcmp (buffer,"TestSomeText");
2370 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
2372 /* Native richedit 2.0 ignores last \par */
2373 es.dwCookie = (DWORD_PTR)&streamText0a;
2375 es.pfnCallback = test_EM_STREAMIN_esCallback;
2376 SendMessage(hwndRichEdit, EM_STREAMIN,
2377 (WPARAM)(SF_RTF), (LPARAM)&es);
2379 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2381 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
2382 result = strcmp (buffer,"TestSomeText");
2384 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
2386 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
2387 es.dwCookie = (DWORD_PTR)&streamText0b;
2389 es.pfnCallback = test_EM_STREAMIN_esCallback;
2390 SendMessage(hwndRichEdit, EM_STREAMIN,
2391 (WPARAM)(SF_RTF), (LPARAM)&es);
2393 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2395 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
2396 result = strcmp (buffer,"TestSomeText\r\n");
2398 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
2400 es.dwCookie = (DWORD_PTR)&streamText1;
2402 es.pfnCallback = test_EM_STREAMIN_esCallback;
2403 SendMessage(hwndRichEdit, EM_STREAMIN,
2404 (WPARAM)(SF_RTF), (LPARAM)&es);
2406 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2408 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
2409 result = strcmp (buffer,"TestSomeText");
2411 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2413 es.dwCookie = (DWORD_PTR)&streamText2;
2414 SendMessage(hwndRichEdit, EM_STREAMIN,
2415 (WPARAM)(SF_RTF), (LPARAM)&es);
2417 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2419 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
2420 ok (strlen(buffer) == 0,
2421 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2423 es.dwCookie = (DWORD_PTR)&streamText3;
2424 SendMessage(hwndRichEdit, EM_STREAMIN,
2425 (WPARAM)(SF_RTF), (LPARAM)&es);
2427 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2429 "EM_STREAMIN: Test 3 returned %ld, expected 9\n", result);
2430 ok (strlen(buffer) == 0,
2431 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
2433 DestroyWindow(hwndRichEdit);
2436 static void test_EM_StreamIn_Undo(void)
2438 /* The purpose of this test is to determine when a EM_StreamIn should be
2439 * undoable. This is important because WM_PASTE currently uses StreamIn and
2440 * pasting should always be undoable but streaming isn't always.
2443 * StreamIn plain text without SFF_SELECTION.
2444 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
2445 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
2446 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
2447 * Feel free to add tests for other text modes or StreamIn things.
2451 HWND hwndRichEdit = new_richedit(NULL);
2454 char buffer[1024] = {0};
2455 const char randomtext[] = "Some text";
2457 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
2459 /* StreamIn, no SFF_SELECTION */
2460 es.dwCookie = nCallbackCount;
2461 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2462 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2463 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2464 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
2465 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2466 result = strcmp (buffer,"test");
2468 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2470 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2471 ok (result == FALSE,
2472 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
2474 /* StreamIn, SFF_SELECTION, but nothing selected */
2475 es.dwCookie = nCallbackCount;
2476 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2477 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2478 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2479 SendMessage(hwndRichEdit, EM_STREAMIN,
2480 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2481 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2482 result = strcmp (buffer,"testSome text");
2484 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2486 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2488 "EM_STREAMIN with SFF_SELECTION but no selection set "
2489 "should create an undo\n");
2491 /* StreamIn, SFF_SELECTION, with a selection */
2492 es.dwCookie = nCallbackCount;
2493 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2494 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2495 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
2496 SendMessage(hwndRichEdit, EM_STREAMIN,
2497 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2498 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2499 result = strcmp (buffer,"Sometesttext");
2501 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2503 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2505 "EM_STREAMIN with SFF_SELECTION and selection set "
2506 "should create an undo\n");
2510 static BOOL is_em_settextex_supported(HWND hwnd)
2512 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
2513 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
2516 static void test_unicode_conversions(void)
2518 static const WCHAR tW[] = {'t',0};
2519 static const WCHAR teW[] = {'t','e',0};
2520 static const WCHAR textW[] = {'t','e','s','t',0};
2521 static const char textA[] = "test";
2525 int is_win9x, em_settextex_supported, ret;
2527 is_win9x = GetVersion() & 0x80000000;
2529 #define set_textA(hwnd, wm_set_text, txt) \
2531 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
2532 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2533 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2534 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2535 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
2537 #define expect_textA(hwnd, wm_get_text, txt) \
2539 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2540 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2541 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2542 memset(bufA, 0xAA, sizeof(bufA)); \
2543 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2544 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2545 ret = lstrcmpA(bufA, txt); \
2546 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
2549 #define set_textW(hwnd, wm_set_text, txt) \
2551 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
2552 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2553 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2554 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2555 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
2557 #define expect_textW(hwnd, wm_get_text, txt) \
2559 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
2560 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2561 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2562 memset(bufW, 0xAA, sizeof(bufW)); \
2565 assert(wm_get_text == EM_GETTEXTEX); \
2566 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2567 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2571 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2572 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
2574 ret = lstrcmpW(bufW, txt); \
2575 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
2577 #define expect_empty(hwnd, wm_get_text) \
2579 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2580 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2581 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2582 memset(bufA, 0xAA, sizeof(bufA)); \
2583 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2584 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
2585 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
2588 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2589 0, 0, 200, 60, 0, 0, 0, 0);
2590 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2592 ret = IsWindowUnicode(hwnd);
2594 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
2596 ok(ret, "RichEdit20W should be unicode under NT\n");
2598 /* EM_SETTEXTEX is supported starting from version 3.0 */
2599 em_settextex_supported = is_em_settextex_supported(hwnd);
2600 trace("EM_SETTEXTEX is %ssupported on this platform\n",
2601 em_settextex_supported ? "" : "NOT ");
2603 expect_empty(hwnd, WM_GETTEXT);
2604 expect_empty(hwnd, EM_GETTEXTEX);
2606 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
2607 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2608 expect_textA(hwnd, WM_GETTEXT, "t");
2609 expect_textA(hwnd, EM_GETTEXTEX, "t");
2610 expect_textW(hwnd, EM_GETTEXTEX, tW);
2612 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
2613 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2614 expect_textA(hwnd, WM_GETTEXT, "te");
2615 expect_textA(hwnd, EM_GETTEXTEX, "te");
2616 expect_textW(hwnd, EM_GETTEXTEX, teW);
2618 set_textA(hwnd, WM_SETTEXT, NULL);
2619 expect_empty(hwnd, WM_GETTEXT);
2620 expect_empty(hwnd, EM_GETTEXTEX);
2623 set_textA(hwnd, WM_SETTEXT, textW);
2625 set_textA(hwnd, WM_SETTEXT, textA);
2626 expect_textA(hwnd, WM_GETTEXT, textA);
2627 expect_textA(hwnd, EM_GETTEXTEX, textA);
2628 expect_textW(hwnd, EM_GETTEXTEX, textW);
2630 if (em_settextex_supported)
2632 set_textA(hwnd, EM_SETTEXTEX, textA);
2633 expect_textA(hwnd, WM_GETTEXT, textA);
2634 expect_textA(hwnd, EM_GETTEXTEX, textA);
2635 expect_textW(hwnd, EM_GETTEXTEX, textW);
2640 set_textW(hwnd, WM_SETTEXT, textW);
2641 expect_textW(hwnd, WM_GETTEXT, textW);
2642 expect_textA(hwnd, WM_GETTEXT, textA);
2643 expect_textW(hwnd, EM_GETTEXTEX, textW);
2644 expect_textA(hwnd, EM_GETTEXTEX, textA);
2646 if (em_settextex_supported)
2648 set_textW(hwnd, EM_SETTEXTEX, textW);
2649 expect_textW(hwnd, WM_GETTEXT, textW);
2650 expect_textA(hwnd, WM_GETTEXT, textA);
2651 expect_textW(hwnd, EM_GETTEXTEX, textW);
2652 expect_textA(hwnd, EM_GETTEXTEX, textA);
2655 DestroyWindow(hwnd);
2657 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
2658 0, 0, 200, 60, 0, 0, 0, 0);
2659 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2661 ret = IsWindowUnicode(hwnd);
2662 ok(!ret, "RichEdit20A should NOT be unicode\n");
2664 set_textA(hwnd, WM_SETTEXT, textA);
2665 expect_textA(hwnd, WM_GETTEXT, textA);
2666 expect_textA(hwnd, EM_GETTEXTEX, textA);
2667 expect_textW(hwnd, EM_GETTEXTEX, textW);
2669 if (em_settextex_supported)
2671 set_textA(hwnd, EM_SETTEXTEX, textA);
2672 expect_textA(hwnd, WM_GETTEXT, textA);
2673 expect_textA(hwnd, EM_GETTEXTEX, textA);
2674 expect_textW(hwnd, EM_GETTEXTEX, textW);
2679 set_textW(hwnd, WM_SETTEXT, textW);
2680 expect_textW(hwnd, WM_GETTEXT, textW);
2681 expect_textA(hwnd, WM_GETTEXT, textA);
2682 expect_textW(hwnd, EM_GETTEXTEX, textW);
2683 expect_textA(hwnd, EM_GETTEXTEX, textA);
2685 if (em_settextex_supported)
2687 set_textW(hwnd, EM_SETTEXTEX, textW);
2688 expect_textW(hwnd, WM_GETTEXT, textW);
2689 expect_textA(hwnd, WM_GETTEXT, textA);
2690 expect_textW(hwnd, EM_GETTEXTEX, textW);
2691 expect_textA(hwnd, EM_GETTEXTEX, textA);
2694 DestroyWindow(hwnd);
2697 static void test_WM_CHAR(void)
2701 const char * char_list = "abc\rabc\r";
2702 const char * expected_content_single = "abcabc";
2703 const char * expected_content_multi = "abc\r\nabc\r\n";
2704 char buffer[64] = {0};
2707 /* single-line control must IGNORE carriage returns */
2708 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2709 0, 0, 200, 60, 0, 0, 0, 0);
2710 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2713 while (*p != '\0') {
2714 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2715 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2716 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2717 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2721 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2722 ret = strcmp(buffer, expected_content_single);
2723 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2725 DestroyWindow(hwnd);
2727 /* multi-line control inserts CR normally */
2728 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
2729 0, 0, 200, 60, 0, 0, 0, 0);
2730 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2733 while (*p != '\0') {
2734 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2735 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2736 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2737 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2741 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2742 ret = strcmp(buffer, expected_content_multi);
2743 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2745 DestroyWindow(hwnd);
2748 static void test_EM_GETTEXTLENGTHEX(void)
2751 GETTEXTLENGTHEX gtl;
2753 const char * base_string = "base string";
2754 const char * test_string = "a\nb\n\n\r\n";
2755 const char * test_string_after = "a";
2756 const char * test_string_2 = "a\rtest\rstring";
2757 char buffer[64] = {0};
2760 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2761 0, 0, 200, 60, 0, 0, 0, 0);
2762 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2764 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2765 gtl.codepage = CP_ACP;
2766 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2767 ok(ret == 0, "ret %d\n",ret);
2769 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2770 gtl.codepage = CP_ACP;
2771 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2772 ok(ret == 0, "ret %d\n",ret);
2774 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
2776 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2777 gtl.codepage = CP_ACP;
2778 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2779 ok(ret == strlen(base_string), "ret %d\n",ret);
2781 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2782 gtl.codepage = CP_ACP;
2783 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2784 ok(ret == strlen(base_string), "ret %d\n",ret);
2786 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
2788 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2789 gtl.codepage = CP_ACP;
2790 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2791 ok(ret == 1, "ret %d\n",ret);
2793 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2794 gtl.codepage = CP_ACP;
2795 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2796 ok(ret == 1, "ret %d\n",ret);
2798 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2799 ret = strcmp(buffer, test_string_after);
2800 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2802 DestroyWindow(hwnd);
2805 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
2806 0, 0, 200, 60, 0, 0, 0, 0);
2807 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2809 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2810 gtl.codepage = CP_ACP;
2811 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2812 ok(ret == 0, "ret %d\n",ret);
2814 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2815 gtl.codepage = CP_ACP;
2816 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2817 ok(ret == 0, "ret %d\n",ret);
2819 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
2821 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2822 gtl.codepage = CP_ACP;
2823 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2824 ok(ret == strlen(base_string), "ret %d\n",ret);
2826 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2827 gtl.codepage = CP_ACP;
2828 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2829 ok(ret == strlen(base_string), "ret %d\n",ret);
2831 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
2833 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2834 gtl.codepage = CP_ACP;
2835 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2836 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
2838 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2839 gtl.codepage = CP_ACP;
2840 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2841 ok(ret == strlen(test_string_2), "ret %d\n",ret);
2843 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
2845 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2846 gtl.codepage = CP_ACP;
2847 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2848 ok(ret == 10, "ret %d\n",ret);
2850 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2851 gtl.codepage = CP_ACP;
2852 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2853 ok(ret == 6, "ret %d\n",ret);
2855 DestroyWindow(hwnd);
2859 /* globals that parent and child access when checking event masks & notifications */
2860 static HWND eventMaskEditHwnd = 0;
2861 static int queriedEventMask;
2862 static int watchForEventMask = 0;
2864 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
2865 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2867 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
2869 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2871 return DefWindowProcA(hwnd, message, wParam, lParam);
2874 /* test event masks in combination with WM_COMMAND */
2875 static void test_eventMask(void)
2880 const char text[] = "foo bar\n";
2883 /* register class to capture WM_COMMAND */
2885 cls.lpfnWndProc = ParentMsgCheckProcA;
2888 cls.hInstance = GetModuleHandleA(0);
2890 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
2891 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
2892 cls.lpszMenuName = NULL;
2893 cls.lpszClassName = "EventMaskParentClass";
2894 if(!RegisterClassA(&cls)) assert(0);
2896 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
2897 0, 0, 200, 60, NULL, NULL, NULL, NULL);
2898 ok (parent != 0, "Failed to create parent window\n");
2900 eventMaskEditHwnd = new_richedit(parent);
2901 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
2903 eventMask = ENM_CHANGE | ENM_UPDATE;
2904 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
2905 ok(ret == ENM_NONE, "wrong event mask\n");
2906 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2907 ok(ret == eventMask, "failed to set event mask\n");
2909 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
2910 queriedEventMask = 0; /* initialize to something other than we expect */
2911 watchForEventMask = EN_CHANGE;
2912 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
2913 ok(ret == TRUE, "failed to set text\n");
2914 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
2915 notification in response to WM_SETTEXT */
2916 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
2917 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
2922 START_TEST( editor )
2927 /* Must explicitly LoadLibrary(). The test has no references to functions in
2928 * RICHED20.DLL, so the linker doesn't actually link to it. */
2929 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
2930 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
2935 test_EM_SCROLLCARET();
2938 test_EM_SETCHARFORMAT();
2939 test_EM_SETTEXTMODE();
2940 test_TM_PLAINTEXT();
2941 test_EM_SETOPTIONS();
2943 test_EM_AUTOURLDETECT();
2944 test_EM_SETUNDOLIMIT();
2946 test_EM_SETTEXTEX();
2947 test_EM_LIMITTEXT();
2948 test_EM_EXLIMITTEXT();
2949 test_EM_GETLIMITTEXT();
2951 test_EM_GETMODIFY();
2955 test_EM_StreamIn_Undo();
2956 test_EM_FORMATRANGE();
2957 test_unicode_conversions();
2958 test_EM_GETTEXTLENGTHEX();
2959 test_EM_REPLACESEL(1);
2960 test_EM_REPLACESEL(0);
2963 /* Set the environment variable WINETEST_RICHED20 to keep windows
2964 * responsive and open for 30 seconds. This is useful for debugging.
2966 * The message pump uses PeekMessage() to empty the queue and then sleeps for
2967 * 50ms before retrying the queue. */
2968 end = time(NULL) + 30;
2969 if (getenv( "WINETEST_RICHED20" )) {
2970 while (time(NULL) < end) {
2971 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
2972 TranslateMessage(&msg);
2973 DispatchMessage(&msg);
2980 OleFlushClipboard();
2981 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());