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},
132 {0, -1, "wineWine wine", 0, -1, 0},
135 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
138 memset(&ft, 0, sizeof(ft));
139 ft.chrg.cpMin = f->start;
140 ft.chrg.cpMax = f->end;
141 ft.lpstrText = f->needle;
142 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
143 ok(findloc == f->expected_loc,
144 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
145 name, id, f->needle, f->start, f->end, f->flags, findloc);
148 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
152 memset(&ft, 0, sizeof(ft));
153 ft.chrg.cpMin = f->start;
154 ft.chrg.cpMax = f->end;
155 ft.lpstrText = f->needle;
156 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
157 ok(findloc == f->expected_loc,
158 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
159 name, id, f->needle, f->start, f->end, f->flags, findloc);
160 ok(ft.chrgText.cpMin == f->expected_loc,
161 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
162 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
163 ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
164 : f->expected_loc + strlen(f->needle)),
165 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n",
166 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
169 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
174 for (i = 0; i < num_tests; i++) {
175 if (find[i]._todo_wine) {
177 check_EM_FINDTEXT(hwnd, name, &find[i], i);
178 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
181 check_EM_FINDTEXT(hwnd, name, &find[i], i);
182 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
187 static void test_EM_FINDTEXT(void)
189 HWND hwndRichEdit = new_richedit(NULL);
191 /* Empty rich edit control */
192 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
193 sizeof(find_tests)/sizeof(struct find_s));
195 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
198 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
199 sizeof(find_tests2)/sizeof(struct find_s));
201 DestroyWindow(hwndRichEdit);
204 static const struct getline_s {
209 {0, 10, "foo bar\r"},
214 /* Buffer smaller than line length */
220 static void test_EM_GETLINE(void)
223 HWND hwndRichEdit = new_richedit(NULL);
224 static const int nBuf = 1024;
225 char dest[1024], origdest[1024];
226 const char text[] = "foo bar\n"
230 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
232 memset(origdest, 0xBB, nBuf);
233 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
236 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
237 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
238 memset(dest, 0xBB, nBuf);
239 *(WORD *) dest = gl[i].buffer_len;
241 /* EM_GETLINE appends a "\r\0" to the end of the line
242 * nCopied counts up to and including the '\r' */
243 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
244 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
246 /* two special cases since a parameter is passed via dest */
247 if (gl[i].buffer_len == 0)
248 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
250 else if (gl[i].buffer_len == 1)
251 ok(dest[0] == gl[i].text[0] && !dest[1] &&
252 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
255 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
256 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
257 ok(!strncmp(dest + expected_bytes_written, origdest
258 + expected_bytes_written, nBuf - expected_bytes_written),
259 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
263 DestroyWindow(hwndRichEdit);
266 static void test_EM_LINELENGTH(void)
268 HWND hwndRichEdit = new_richedit(NULL);
274 int offset_test[10][2] = {
289 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
291 for (i = 0; i < 10; i++) {
292 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
293 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
294 offset_test[i][0], result, offset_test[i][1]);
297 DestroyWindow(hwndRichEdit);
300 static int get_scroll_pos_y(HWND hwnd)
303 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
304 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
308 static void move_cursor(HWND hwnd, long charindex)
311 cr.cpMax = charindex;
312 cr.cpMin = charindex;
313 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
316 static void line_scroll(HWND hwnd, int amount)
318 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
321 static void test_EM_SCROLLCARET(void)
324 HWND hwndRichEdit = new_richedit(NULL);
325 const char text[] = "aa\n"
326 "this is a long line of text that should be longer than the "
335 /* Can't verify this */
336 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
338 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
340 /* Caret above visible window */
341 line_scroll(hwndRichEdit, 3);
342 prevY = get_scroll_pos_y(hwndRichEdit);
343 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
344 curY = get_scroll_pos_y(hwndRichEdit);
345 ok(prevY != curY, "%d == %d\n", prevY, curY);
347 /* Caret below visible window */
348 move_cursor(hwndRichEdit, sizeof(text) - 1);
349 line_scroll(hwndRichEdit, -3);
350 prevY = get_scroll_pos_y(hwndRichEdit);
351 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
352 curY = get_scroll_pos_y(hwndRichEdit);
353 ok(prevY != curY, "%d == %d\n", prevY, curY);
355 /* Caret in visible window */
356 move_cursor(hwndRichEdit, sizeof(text) - 2);
357 prevY = get_scroll_pos_y(hwndRichEdit);
358 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
359 curY = get_scroll_pos_y(hwndRichEdit);
360 ok(prevY == curY, "%d != %d\n", prevY, curY);
362 /* Caret still in visible window */
363 line_scroll(hwndRichEdit, -1);
364 prevY = get_scroll_pos_y(hwndRichEdit);
365 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
366 curY = get_scroll_pos_y(hwndRichEdit);
367 ok(prevY == curY, "%d != %d\n", prevY, curY);
369 DestroyWindow(hwndRichEdit);
372 static void test_EM_SETCHARFORMAT(void)
374 HWND hwndRichEdit = new_richedit(NULL);
378 /* Invalid flags, CHARFORMAT2 structure blanked out */
379 memset(&cf2, 0, sizeof(cf2));
380 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
382 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
384 /* A valid flag, CHARFORMAT2 structure blanked out */
385 memset(&cf2, 0, sizeof(cf2));
386 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
388 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
390 /* A valid flag, CHARFORMAT2 structure blanked out */
391 memset(&cf2, 0, sizeof(cf2));
392 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
394 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
396 /* A valid flag, CHARFORMAT2 structure blanked out */
397 memset(&cf2, 0, sizeof(cf2));
398 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
400 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
402 /* A valid flag, CHARFORMAT2 structure blanked out */
403 memset(&cf2, 0, sizeof(cf2));
404 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
406 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
408 /* Invalid flags, CHARFORMAT2 structure minimally filled */
409 memset(&cf2, 0, sizeof(cf2));
410 cf2.cbSize = sizeof(CHARFORMAT2);
411 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
413 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
415 /* A valid flag, CHARFORMAT2 structure minimally filled */
416 memset(&cf2, 0, sizeof(cf2));
417 cf2.cbSize = sizeof(CHARFORMAT2);
418 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
420 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
422 /* A valid flag, CHARFORMAT2 structure minimally filled */
423 memset(&cf2, 0, sizeof(cf2));
424 cf2.cbSize = sizeof(CHARFORMAT2);
425 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
427 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
429 /* A valid flag, CHARFORMAT2 structure minimally filled */
430 memset(&cf2, 0, sizeof(cf2));
431 cf2.cbSize = sizeof(CHARFORMAT2);
432 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
434 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
436 /* A valid flag, CHARFORMAT2 structure minimally filled */
437 memset(&cf2, 0, sizeof(cf2));
438 cf2.cbSize = sizeof(CHARFORMAT2);
439 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
441 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
443 cf2.cbSize = sizeof(CHARFORMAT2);
444 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
447 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
448 cf2.cbSize = sizeof(CHARFORMAT2);
449 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
451 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
452 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
454 /* wParam==0 is default char format, does not set modify */
455 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
456 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
457 ok(rc == 0, "Text marked as modified, expected not modified!\n");
458 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
459 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
460 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
461 ok(rc == 0, "Text marked as modified, expected not modified!\n");
463 /* wParam==SCF_SELECTION sets modify if nonempty selection */
464 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
465 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
466 ok(rc == 0, "Text marked as modified, expected not modified!\n");
467 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
468 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
469 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
470 ok(rc == 0, "Text marked as modified, expected not modified!\n");
472 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
473 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
474 ok(rc == 0, "Text marked as modified, expected not modified!\n");
475 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
476 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
477 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
478 ok(rc == 0, "Text marked as modified, expected not modified!\n");
479 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
480 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
481 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
482 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
483 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
485 /* wParam==SCF_ALL sets modify regardless of whether text is present */
486 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
487 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
488 ok(rc == 0, "Text marked as modified, expected not modified!\n");
489 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
490 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
491 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
492 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
494 DestroyWindow(hwndRichEdit);
497 static void test_EM_SETTEXTMODE(void)
499 HWND hwndRichEdit = new_richedit(NULL);
500 CHARFORMAT2 cf2, cf2test;
504 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
505 /*Insert text into the control*/
507 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
509 /*Attempt to change the control to plain text mode*/
510 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
511 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
513 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
514 If rich text is pasted, it should have the same formatting as the rest
515 of the text in the control*/
518 *NOTE: If the default text was already italicized, the test will simply
519 reverse; in other words, it will copy a regular "wine" into a plain
520 text window that uses an italicized format*/
521 cf2.cbSize = sizeof(CHARFORMAT2);
522 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
525 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
526 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
528 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
529 ok(rc == 0, "Text marked as modified, expected not modified!\n");
531 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
532 however, SCF_ALL has been implemented*/
533 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
534 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
536 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
537 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
539 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
541 /*Select the string "wine"*/
544 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
546 /*Copy the italicized "wine" to the clipboard*/
547 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
549 /*Reset the formatting to default*/
550 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
551 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
552 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
554 /*Clear the text in the control*/
555 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
557 /*Switch to Plain Text Mode*/
558 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
559 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
561 /*Input "wine" again in normal format*/
562 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
564 /*Paste the italicized "wine" into the control*/
565 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
567 /*Select a character from the first "wine" string*/
570 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
572 /*Retrieve its formatting*/
573 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
576 /*Select a character from the second "wine" string*/
579 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
581 /*Retrieve its formatting*/
582 cf2test.cbSize = sizeof(CHARFORMAT2);
583 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
586 /*Compare the two formattings*/
587 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
588 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
589 cf2.dwEffects, cf2test.dwEffects);
590 /*Test TM_RICHTEXT by: switching back to Rich Text mode
591 printing "wine" in the current format(normal)
592 pasting "wine" from the clipboard(italicized)
593 comparing the two formats(should differ)*/
595 /*Attempt to switch with text in control*/
596 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
597 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
600 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
602 /*Switch into Rich Text mode*/
603 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
604 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
606 /*Print "wine" in normal formatting into the control*/
607 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
609 /*Paste italicized "wine" into the control*/
610 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
612 /*Select text from the first "wine" string*/
615 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
617 /*Retrieve its formatting*/
618 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
621 /*Select text from the second "wine" string*/
624 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
626 /*Retrieve its formatting*/
627 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
630 /*Test that the two formattings are not the same*/
631 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
632 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
633 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
635 DestroyWindow(hwndRichEdit);
638 static void test_TM_PLAINTEXT(void)
640 /*Tests plain text properties*/
642 HWND hwndRichEdit = new_richedit(NULL);
643 CHARFORMAT2 cf2, cf2test;
647 /*Switch to plain text mode*/
649 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
650 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
652 /*Fill control with text*/
654 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
656 /*Select some text and bold it*/
660 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
661 cf2.cbSize = sizeof(CHARFORMAT2);
662 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
665 cf2.dwMask = CFM_BOLD | cf2.dwMask;
666 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
668 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
669 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
671 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
672 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
674 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
675 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
677 /*Get the formatting of those characters*/
679 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
681 /*Get the formatting of some other characters*/
682 cf2test.cbSize = sizeof(CHARFORMAT2);
685 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
686 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
688 /*Test that they are the same as plain text allows only one formatting*/
690 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
691 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
692 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
694 /*Fill the control with a "wine" string, which when inserted will be bold*/
696 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
698 /*Copy the bolded "wine" string*/
702 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
703 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
705 /*Swap back to rich text*/
707 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
708 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
710 /*Set the default formatting to bold italics*/
712 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
713 cf2.dwMask |= CFM_ITALIC;
714 cf2.dwEffects ^= CFE_ITALIC;
715 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
716 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
718 /*Set the text in the control to "wine", which will be bold and italicized*/
720 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
722 /*Paste the plain text "wine" string, which should take the insert
723 formatting, which at the moment is bold italics*/
725 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
727 /*Select the first "wine" string and retrieve its formatting*/
731 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
732 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
734 /*Select the second "wine" string and retrieve its formatting*/
738 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
739 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
741 /*Compare the two formattings. They should be the same.*/
743 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
744 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
745 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
746 DestroyWindow(hwndRichEdit);
749 static void test_WM_GETTEXT(void)
751 HWND hwndRichEdit = new_richedit(NULL);
752 static const char text[] = "Hello. My name is RichEdit!";
753 static const char text2[] = "Hello. My name is RichEdit!\r";
754 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
755 char buffer[1024] = {0};
758 /* Baseline test with normal-sized buffer */
759 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
760 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
761 ok(result == lstrlen(buffer),
762 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
763 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
764 result = strcmp(buffer,text);
766 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
768 /* Test for returned value of WM_GETTEXTLENGTH */
769 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
770 ok(result == lstrlen(text),
771 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
772 result, lstrlen(text));
774 /* Test for behavior in overflow case */
775 memset(buffer, 0, 1024);
776 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
778 result == lstrlenA(text) - 1, /* XP, win2k3 */
779 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
780 result = strcmp(buffer,text);
782 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
784 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
786 /* Baseline test with normal-sized buffer and carriage return */
787 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
788 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
789 ok(result == lstrlen(buffer),
790 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
791 result = strcmp(buffer,text2_after);
793 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
795 /* Test for returned value of WM_GETTEXTLENGTH */
796 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
797 ok(result == lstrlen(text2_after),
798 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
799 result, lstrlen(text2_after));
801 /* Test for behavior of CRLF conversion in case of overflow */
802 memset(buffer, 0, 1024);
803 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
805 result == lstrlenA(text2) - 1, /* XP, win2k3 */
806 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
807 result = strcmp(buffer,text2);
809 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
811 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
813 DestroyWindow(hwndRichEdit);
816 static void test_EM_GETTEXTRANGE(void)
818 HWND hwndRichEdit = new_richedit(NULL);
819 const char * text1 = "foo bar\r\nfoo bar";
820 const char * text2 = "foo bar\rfoo bar";
821 const char * expect = "bar\rfoo";
822 char buffer[1024] = {0};
824 TEXTRANGEA textRange;
826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
828 textRange.lpstrText = buffer;
829 textRange.chrg.cpMin = 4;
830 textRange.chrg.cpMax = 11;
831 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
832 ok(result == 7, "EM_GETTEXTRANGE returned %ld, expected %d\n",
833 result, strlen(expect));
834 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
836 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
838 textRange.lpstrText = buffer;
839 textRange.chrg.cpMin = 4;
840 textRange.chrg.cpMax = 11;
841 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
842 ok(result == 7, "EM_GETTEXTRANGE returned %ld, expected %d\n",
843 result, strlen(expect));
844 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
846 DestroyWindow(hwndRichEdit);
849 static void test_EM_GETSELTEXT(void)
851 HWND hwndRichEdit = new_richedit(NULL);
852 const char * text1 = "foo bar\r\nfoo bar";
853 const char * text2 = "foo bar\rfoo bar";
854 const char * expect = "bar\rfoo";
855 char buffer[1024] = {0};
858 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
860 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
861 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
862 ok(result == 7, "EM_GETTEXTRANGE returned %ld, expected %d\n",
863 result, strlen(expect));
864 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
866 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
868 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
869 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
870 ok(result == 7, "EM_GETTEXTRANGE returned %ld, expected %d\n",
871 result, strlen(expect));
872 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
874 DestroyWindow(hwndRichEdit);
877 /* FIXME: need to test unimplemented options and robustly test wparam */
878 static void test_EM_SETOPTIONS(void)
880 HWND hwndRichEdit = new_richedit(NULL);
881 static const char text[] = "Hello. My name is RichEdit!";
882 char buffer[1024] = {0};
884 /* NEGATIVE TESTING - NO OPTIONS SET */
885 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
886 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
888 /* testing no readonly by sending 'a' to the control*/
889 SetFocus(hwndRichEdit);
890 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
891 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
893 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
894 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
896 /* READONLY - sending 'a' to the control */
897 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
898 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
899 SetFocus(hwndRichEdit);
900 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
901 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
902 ok(buffer[0]==text[0],
903 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
905 DestroyWindow(hwndRichEdit);
908 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
910 CHARFORMAT2W text_format;
911 int link_present = 0;
912 text_format.cbSize = sizeof(text_format);
913 SendMessage(hwnd, EM_SETSEL, 0, 1);
914 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
915 link_present = text_format.dwEffects & CFE_LINK;
917 { /* control text is url; should get CFE_LINK */
918 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
922 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
926 static HWND new_static_wnd(HWND parent) {
927 return new_window("Static", 0, parent);
930 static void test_EM_AUTOURLDETECT(void)
937 {"http://www.winehq.org", 1},
938 {"http//winehq.org", 0},
939 {"ww.winehq.org", 0},
940 {"www.winehq.org", 1},
941 {"ftp://192.168.1.1", 1},
942 {"ftp//192.168.1.1", 0},
943 {"mailto:your@email.com", 1},
944 {"prospero:prosperoserver", 1},
946 {"news:newserver", 1},
947 {"wais:waisserver", 1}
952 HWND hwndRichEdit, parent;
954 parent = new_static_wnd(NULL);
955 hwndRichEdit = new_richedit(parent);
956 /* Try and pass EM_AUTOURLDETECT some test wParam values */
957 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
958 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
959 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
960 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
961 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
962 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
963 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
964 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
965 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
966 /* for each url, check the text to see if CFE_LINK effect is present */
967 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
968 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
969 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
970 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
971 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
972 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
973 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
974 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
975 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
977 DestroyWindow(hwndRichEdit);
978 DestroyWindow(parent);
981 static void test_EM_SCROLL(void)
984 int r; /* return value */
985 int expr; /* expected return value */
986 HWND hwndRichEdit = new_richedit(NULL);
987 int y_before, y_after; /* units of lines of text */
989 /* test a richedit box containing a single line of text */
990 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
992 for (i = 0; i < 4; i++) {
993 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
995 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
996 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
997 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
998 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
999 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
1000 "(i == %d)\n", y_after, i);
1004 * test a richedit box that will scroll. There are two general
1005 * cases: the case without any long lines and the case with a long
1008 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
1010 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
1012 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
1013 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1014 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1015 "LONG LINE \nb\nc\nd\ne");
1016 for (j = 0; j < 12; j++) /* reset scroll position to top */
1017 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
1019 /* get first visible line */
1020 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1021 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
1023 /* get new current first visible line */
1024 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1026 ok(((r & 0xffffff00) == 0x00010000) &&
1027 ((r & 0x000000ff) != 0x00000000),
1028 "EM_SCROLL page down didn't scroll by a small positive number of "
1029 "lines (r == 0x%08x)\n", r);
1030 ok(y_after > y_before, "EM_SCROLL page down not functioning "
1031 "(line %d scrolled to line %d\n", y_before, y_after);
1035 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
1036 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1037 ok(((r & 0xffffff00) == 0x0001ff00),
1038 "EM_SCROLL page up didn't scroll by a small negative number of lines "
1039 "(r == 0x%08x)\n", r);
1040 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
1041 "%d scrolled to line %d\n", y_before, y_after);
1045 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
1047 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1049 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
1050 "(r == 0x%08x)\n", r);
1051 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
1052 "1 line (%d scrolled to %d)\n", y_before, y_after);
1056 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
1058 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1060 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
1061 "(r == 0x%08x)\n", r);
1062 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
1063 "line (%d scrolled to %d)\n", y_before, y_after);
1067 r = SendMessage(hwndRichEdit, EM_SCROLL,
1068 SB_LINEUP, 0); /* lineup beyond top */
1070 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1073 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
1074 ok(y_before == y_after,
1075 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
1079 r = SendMessage(hwndRichEdit, EM_SCROLL,
1080 SB_PAGEUP, 0);/*page up beyond top */
1082 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1085 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
1086 ok(y_before == y_after,
1087 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
1089 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
1090 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
1091 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1092 r = SendMessage(hwndRichEdit, EM_SCROLL,
1093 SB_PAGEDOWN, 0); /* page down beyond bot */
1094 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1097 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
1098 ok(y_before == y_after,
1099 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
1102 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1103 SendMessage(hwndRichEdit, EM_SCROLL,
1104 SB_LINEDOWN, 0); /* line down beyond bot */
1105 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1108 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
1109 ok(y_before == y_after,
1110 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
1113 DestroyWindow(hwndRichEdit);
1116 static void test_EM_SETUNDOLIMIT(void)
1118 /* cases we test for:
1119 * default behaviour - limiting at 100 undo's
1120 * undo disabled - setting a limit of 0
1121 * undo limited - undo limit set to some to some number, like 2
1122 * bad input - sending a negative number should default to 100 undo's */
1124 HWND hwndRichEdit = new_richedit(NULL);
1129 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1132 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1133 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
1134 also, multiple pastes don't combine like WM_CHAR would */
1135 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1137 /* first case - check the default */
1138 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1139 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
1140 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1141 for (i=0; i<100; i++) /* Undo 100 of them */
1142 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1143 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1144 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
1146 /* second case - cannot undo */
1147 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
1148 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
1149 SendMessage(hwndRichEdit,
1150 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
1151 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1152 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
1154 /* third case - set it to an arbitrary number */
1155 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
1156 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
1157 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1158 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1159 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1160 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
1161 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
1162 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
1163 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1164 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1165 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
1166 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1167 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1168 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
1170 /* fourth case - setting negative numbers should default to 100 undos */
1171 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1172 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
1174 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
1176 DestroyWindow(hwndRichEdit);
1179 static void test_ES_PASSWORD(void)
1181 /* This isn't hugely testable, so we're just going to run it through its paces */
1183 HWND hwndRichEdit = new_richedit(NULL);
1186 /* First, check the default of a regular control */
1187 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1189 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
1191 /* Now, set it to something normal */
1192 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
1193 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1195 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1197 /* Now, set it to something odd */
1198 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
1199 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1201 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1202 DestroyWindow(hwndRichEdit);
1205 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
1210 char** str = (char**)dwCookie;
1213 memcpy(*str, pbBuff, *pcb);
1219 static void test_WM_SETTEXT()
1221 HWND hwndRichEdit = new_richedit(NULL);
1222 const char * TestItem1 = "TestSomeText";
1223 const char * TestItem2 = "TestSomeText\r";
1224 const char * TestItem2_after = "TestSomeText\r\n";
1225 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
1226 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
1227 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
1228 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
1229 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
1230 const char * TestItem5_after = "TestSomeText TestSomeText";
1231 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
1232 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
1233 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
1234 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
1236 char buf[1024] = {0};
1241 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1242 any solitary \r to be converted to \r\n on return. Properly paired
1243 \r\n are not affected. It also shows that the special sequence \r\r\n
1244 gets converted to a single space.
1247 #define TEST_SETTEXT(a, b) \
1248 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
1249 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
1250 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
1251 ok (result == lstrlen(buf), \
1252 "WM_GETTEXT returned %ld instead of expected %u\n", \
1253 result, lstrlen(buf)); \
1254 result = strcmp(b, buf); \
1256 "WM_SETTEXT round trip: strcmp = %ld\n", result);
1258 TEST_SETTEXT(TestItem1, TestItem1)
1259 TEST_SETTEXT(TestItem2, TestItem2_after)
1260 TEST_SETTEXT(TestItem3, TestItem3_after)
1261 TEST_SETTEXT(TestItem3_after, TestItem3_after)
1262 TEST_SETTEXT(TestItem4, TestItem4_after)
1263 TEST_SETTEXT(TestItem5, TestItem5_after)
1264 TEST_SETTEXT(TestItem6, TestItem6_after)
1265 TEST_SETTEXT(TestItem7, TestItem7_after)
1267 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
1268 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1270 es.dwCookie = (DWORD_PTR)&p;
1272 es.pfnCallback = test_WM_SETTEXT_esCallback;
1273 memset(buf, 0, sizeof(buf));
1274 SendMessage(hwndRichEdit, EM_STREAMOUT,
1275 (WPARAM)(SF_RTF), (LPARAM)&es);
1276 trace("EM_STREAMOUT produced: \n%s\n", buf);
1277 TEST_SETTEXT(buf, TestItem1)
1280 DestroyWindow(hwndRichEdit);
1283 static void test_EM_STREAMOUT(void)
1285 HWND hwndRichEdit = new_richedit(NULL);
1288 char buf[1024] = {0};
1291 const char * TestItem1 = "TestSomeText";
1292 const char * TestItem2 = "TestSomeText\r";
1293 const char * TestItem3 = "TestSomeText\r\n";
1295 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1297 es.dwCookie = (DWORD_PTR)&p;
1299 es.pfnCallback = test_WM_SETTEXT_esCallback;
1300 memset(buf, 0, sizeof(buf));
1301 SendMessage(hwndRichEdit, EM_STREAMOUT,
1302 (WPARAM)(SF_TEXT), (LPARAM)&es);
1304 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
1305 ok(strcmp(buf, TestItem1) == 0,
1306 "streamed text different, got %s\n", buf);
1308 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
1310 es.dwCookie = (DWORD_PTR)&p;
1312 es.pfnCallback = test_WM_SETTEXT_esCallback;
1313 memset(buf, 0, sizeof(buf));
1314 SendMessage(hwndRichEdit, EM_STREAMOUT,
1315 (WPARAM)(SF_TEXT), (LPARAM)&es);
1317 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
1318 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
1319 ok(strcmp(buf, TestItem3) == 0,
1320 "streamed text different from, got %s\n", buf);
1321 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
1323 es.dwCookie = (DWORD_PTR)&p;
1325 es.pfnCallback = test_WM_SETTEXT_esCallback;
1326 memset(buf, 0, sizeof(buf));
1327 SendMessage(hwndRichEdit, EM_STREAMOUT,
1328 (WPARAM)(SF_TEXT), (LPARAM)&es);
1330 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
1331 ok(strcmp(buf, TestItem3) == 0,
1332 "streamed text different, got %s\n", buf);
1334 DestroyWindow(hwndRichEdit);
1337 static void test_EM_SETTEXTEX(void)
1339 HWND hwndRichEdit = new_richedit(NULL);
1342 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1344 'T', 'e', 'x', 't', 0};
1345 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1349 const char * TestItem2_after = "TestSomeText\r\n";
1350 WCHAR TestItem3[] = {'T', 'e', 's', 't',
1353 '\r','\n','\r','\n', 0};
1354 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
1358 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
1362 WCHAR TestItem4[] = {'T', 'e', 's', 't',
1365 '\r','\r','\n','\r',
1367 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
1371 #define MAX_BUF_LEN 1024
1372 WCHAR buf[MAX_BUF_LEN];
1378 setText.codepage = 1200; /* no constant for unicode */
1379 getText.codepage = 1200; /* no constant for unicode */
1380 getText.cb = MAX_BUF_LEN;
1381 getText.flags = GT_DEFAULT;
1382 getText.lpDefaultChar = NULL;
1383 getText.lpUsedDefChar = NULL;
1386 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1387 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1388 ok(lstrcmpW(buf, TestItem1) == 0,
1389 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1391 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1392 convert \r to \r\n on return
1394 setText.codepage = 1200; /* no constant for unicode */
1395 getText.codepage = 1200; /* no constant for unicode */
1396 getText.cb = MAX_BUF_LEN;
1397 getText.flags = GT_DEFAULT;
1398 getText.lpDefaultChar = NULL;
1399 getText.lpUsedDefChar = NULL;
1401 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1402 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1403 ok(lstrcmpW(buf, TestItem2) == 0,
1404 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1406 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1407 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1408 ok(strcmp((const char *)buf, TestItem2_after) == 0,
1409 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1411 /* Baseline test for just-enough buffer space for string */
1412 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1413 getText.codepage = 1200; /* no constant for unicode */
1414 getText.flags = GT_DEFAULT;
1415 getText.lpDefaultChar = NULL;
1416 getText.lpUsedDefChar = NULL;
1417 memset(buf, 0, MAX_BUF_LEN);
1418 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1419 ok(lstrcmpW(buf, TestItem2) == 0,
1420 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1422 /* When there is enough space for one character, but not both, of the CRLF
1423 pair at the end of the string, the CR is not copied at all. That is,
1424 the caller must not see CRLF pairs truncated to CR at the end of the
1427 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1428 getText.codepage = 1200; /* no constant for unicode */
1429 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
1430 getText.lpDefaultChar = NULL;
1431 getText.lpUsedDefChar = NULL;
1432 memset(buf, 0, MAX_BUF_LEN);
1433 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1434 ok(lstrcmpW(buf, TestItem1) == 0,
1435 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1438 /* \r\n pairs get changed into \r */
1439 setText.codepage = 1200; /* no constant for unicode */
1440 getText.codepage = 1200; /* no constant for unicode */
1441 getText.cb = MAX_BUF_LEN;
1442 getText.flags = GT_DEFAULT;
1443 getText.lpDefaultChar = NULL;
1444 getText.lpUsedDefChar = NULL;
1446 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
1447 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1448 ok(lstrcmpW(buf, TestItem3_after) == 0,
1449 "EM_SETTEXTEX did not convert properly\n");
1451 /* \n also gets changed to \r */
1452 setText.codepage = 1200; /* no constant for unicode */
1453 getText.codepage = 1200; /* no constant for unicode */
1454 getText.cb = MAX_BUF_LEN;
1455 getText.flags = GT_DEFAULT;
1456 getText.lpDefaultChar = NULL;
1457 getText.lpUsedDefChar = NULL;
1459 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
1460 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1461 ok(lstrcmpW(buf, TestItem3_after) == 0,
1462 "EM_SETTEXTEX did not convert properly\n");
1464 /* \r\r\n gets changed into single space */
1465 setText.codepage = 1200; /* no constant for unicode */
1466 getText.codepage = 1200; /* no constant for unicode */
1467 getText.cb = MAX_BUF_LEN;
1468 getText.flags = GT_DEFAULT;
1469 getText.lpDefaultChar = NULL;
1470 getText.lpUsedDefChar = NULL;
1472 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
1473 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1474 ok(lstrcmpW(buf, TestItem4_after) == 0,
1475 "EM_SETTEXTEX did not convert properly\n");
1477 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1478 (WPARAM)&setText, (LPARAM) NULL);
1479 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1482 "EM_SETTEXTEX returned %d, instead of 1\n",result);
1483 ok(lstrlenW(buf) == 0,
1484 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1486 /* put some text back */
1488 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1489 /* select some text */
1492 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1493 /* replace current selection */
1494 setText.flags = ST_SELECTION;
1495 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1496 (WPARAM)&setText, (LPARAM) NULL);
1498 "EM_SETTEXTEX with NULL lParam to replace selection"
1499 " with no text should return 0. Got %i\n",
1502 /* put some text back */
1504 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1505 /* select some text */
1508 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1509 /* replace current selection */
1510 setText.flags = ST_SELECTION;
1511 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1512 (WPARAM)&setText, (LPARAM) TestItem1);
1514 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1515 ok(result == lstrlenW(TestItem1),
1516 "EM_SETTEXTEX with NULL lParam to replace selection"
1517 " with no text should return 0. Got %i\n",
1519 ok(lstrlenW(buf) == 22,
1520 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1523 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
1524 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
1526 es.dwCookie = (DWORD_PTR)&p;
1528 es.pfnCallback = test_WM_SETTEXT_esCallback;
1529 memset(buf, 0, sizeof(buf));
1530 SendMessage(hwndRichEdit, EM_STREAMOUT,
1531 (WPARAM)(SF_RTF), (LPARAM)&es);
1532 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
1534 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
1535 getText.codepage = 1200; /* no constant for unicode */
1536 getText.cb = MAX_BUF_LEN;
1537 getText.flags = GT_DEFAULT;
1538 getText.lpDefaultChar = NULL;
1539 getText.lpUsedDefChar = NULL;
1542 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
1543 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1544 ok(lstrcmpW(buf, TestItem1) == 0,
1545 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1548 DestroyWindow(hwndRichEdit);
1551 static void test_EM_LIMITTEXT(void)
1555 HWND hwndRichEdit = new_richedit(NULL);
1557 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1558 * about setting the length to -1 for multiline edit controls doesn't happen.
1561 /* Don't check default gettextlimit case. That's done in other tests */
1563 /* Set textlimit to 100 */
1564 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1565 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1567 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1569 /* Set textlimit to 0 */
1570 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1571 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1573 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1575 /* Set textlimit to -1 */
1576 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1577 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1579 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1581 /* Set textlimit to -2 */
1582 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1583 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1585 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1587 DestroyWindow (hwndRichEdit);
1591 static void test_EM_EXLIMITTEXT(void)
1593 int i, selBegin, selEnd, len1, len2;
1595 char text[1024 + 1];
1596 char buffer[1024 + 1];
1597 int textlimit = 0; /* multiple of 100 */
1598 HWND hwndRichEdit = new_richedit(NULL);
1600 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1601 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1604 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1605 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1607 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1610 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1611 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1613 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1615 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1616 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1617 /* default for WParam = 0 */
1618 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1620 textlimit = sizeof(text)-1;
1621 memset(text, 'W', textlimit);
1622 text[sizeof(text)-1] = 0;
1623 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1624 /* maxed out text */
1625 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1627 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1628 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1629 len1 = selEnd - selBegin;
1631 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1632 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1633 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1634 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1635 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1636 len2 = selEnd - selBegin;
1639 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1642 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1643 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1644 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1645 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1646 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1647 len1 = selEnd - selBegin;
1650 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1653 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1654 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1655 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1656 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1657 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1658 len2 = selEnd - selBegin;
1661 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1664 /* set text up to the limit, select all the text, then add a char */
1666 memset(text, 'W', textlimit);
1667 text[textlimit] = 0;
1668 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1669 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1670 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1671 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1672 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1673 result = strcmp(buffer, "A");
1674 ok(0 == result, "got string = \"%s\"\n", buffer);
1676 /* WM_SETTEXT not limited */
1678 memset(text, 'W', textlimit);
1679 text[textlimit] = 0;
1680 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1681 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1682 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1684 ok(10 == i, "expected 10 chars\n");
1685 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1686 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1688 /* try inserting more text at end */
1689 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1690 ok(0 == i, "WM_CHAR wasn't processed\n");
1691 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1693 ok(10 == i, "expected 10 chars, got %i\n", i);
1694 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1695 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1697 /* try inserting text at beginning */
1698 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1699 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1700 ok(0 == i, "WM_CHAR wasn't processed\n");
1701 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1703 ok(10 == i, "expected 10 chars, got %i\n", i);
1704 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1705 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1707 /* WM_CHAR is limited */
1709 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1710 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1711 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1712 ok(0 == i, "WM_CHAR wasn't processed\n");
1713 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1714 ok(0 == i, "WM_CHAR wasn't processed\n");
1715 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1717 ok(1 == i, "expected 1 chars, got %i instead\n", i);
1719 DestroyWindow(hwndRichEdit);
1722 static void test_EM_GETLIMITTEXT(void)
1725 HWND hwndRichEdit = new_richedit(NULL);
1727 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1728 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1730 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1731 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1732 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1734 DestroyWindow(hwndRichEdit);
1737 static void test_WM_SETFONT(void)
1739 /* There is no invalid input or error conditions for this function.
1740 * NULL wParam and lParam just fall back to their default values
1741 * It should be noted that even if you use a gibberish name for your fonts
1742 * here, it will still work because the name is stored. They will display as
1743 * System, but will report their name to be whatever they were created as */
1745 HWND hwndRichEdit = new_richedit(NULL);
1746 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1747 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1748 FF_DONTCARE, "Marlett");
1749 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1750 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1751 FF_DONTCARE, "MS Sans Serif");
1752 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1753 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1754 FF_DONTCARE, "Courier");
1755 LOGFONTA sentLogFont;
1756 CHARFORMAT2A returnedCF2A;
1758 returnedCF2A.cbSize = sizeof(returnedCF2A);
1760 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1761 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1762 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1764 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1765 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1766 "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1767 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1769 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1770 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1771 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1772 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1773 "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1774 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1776 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1777 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1778 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1779 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1780 "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1781 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1783 /* This last test is special since we send in NULL. We clear the variables
1784 * and just compare to "System" instead of the sent in font name. */
1785 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1786 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1787 returnedCF2A.cbSize = sizeof(returnedCF2A);
1789 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1790 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1791 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1792 ok (!strcmp("System",returnedCF2A.szFaceName),
1793 "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1795 DestroyWindow(hwndRichEdit);
1799 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1804 const char** str = (const char**)dwCookie;
1805 int size = strlen(*str);
1806 if(size > 3) /* let's make it piecemeal for fun */
1813 memcpy(pbBuff, *str, *pcb);
1819 static void test_EM_GETMODIFY(void)
1821 HWND hwndRichEdit = new_richedit(NULL);
1824 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1826 'T', 'e', 'x', 't', 0};
1827 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1829 'O', 't', 'h', 'e', 'r',
1830 'T', 'e', 'x', 't', 0};
1831 const char* streamText = "hello world";
1836 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1837 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1838 FF_DONTCARE, "Courier");
1840 setText.codepage = 1200; /* no constant for unicode */
1841 setText.flags = ST_KEEPUNDO;
1844 /* modify flag shouldn't be set when richedit is first created */
1845 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1847 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1849 /* setting modify flag should actually set it */
1850 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1851 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1853 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1855 /* clearing modify flag should actually clear it */
1856 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1857 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1859 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1861 /* setting font doesn't change modify flag */
1862 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1863 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1864 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1866 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1868 /* setting text should set modify flag */
1869 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1870 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1871 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1873 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1875 /* undo previous text doesn't reset modify flag */
1876 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1877 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1879 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1881 /* set text with no flag to keep undo stack should not set modify flag */
1882 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1884 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1885 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1887 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1889 /* WM_SETTEXT doesn't modify */
1890 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1891 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1892 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1894 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1896 /* clear the text */
1897 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1898 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1899 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1901 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1904 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1905 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1906 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1907 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1908 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1910 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1912 /* copy/paste text 1 */
1913 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1914 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1915 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1916 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1917 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1919 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1921 /* copy/paste text 2 */
1922 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1923 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1924 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1925 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1926 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1927 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1929 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1932 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1933 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1934 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1935 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1937 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1940 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1941 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1942 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1943 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1945 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1947 /* set char format */
1948 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1949 cf2.cbSize = sizeof(CHARFORMAT2);
1950 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1952 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1953 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1954 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1955 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1956 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
1957 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1959 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1961 /* set para format */
1962 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1963 pf2.cbSize = sizeof(PARAFORMAT2);
1964 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1966 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1967 pf2.wAlignment = PFA_RIGHT;
1968 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1969 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1971 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1974 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1975 es.dwCookie = (DWORD_PTR)&streamText;
1977 es.pfnCallback = test_EM_GETMODIFY_esCallback;
1978 SendMessage(hwndRichEdit, EM_STREAMIN,
1979 (WPARAM)(SF_TEXT), (LPARAM)&es);
1980 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1982 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1984 DestroyWindow(hwndRichEdit);
1990 long expected_retval;
1991 int expected_getsel_start;
1992 int expected_getsel_end;
1993 int _exsetsel_todo_wine;
1994 int _getsel_todo_wine;
1997 const struct exsetsel_s exsetsel_tests[] = {
1999 {5, 10, 10, 5, 10, 0, 0},
2000 {15, 17, 17, 15, 17, 0, 0},
2001 /* test cpMax > strlen() */
2002 {0, 100, 18, 0, 18, 0, 1},
2003 /* test cpMin == cpMax */
2004 {5, 5, 5, 5, 5, 0, 0},
2005 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
2006 {-1, 0, 5, 5, 5, 0, 0},
2007 {-1, 17, 5, 5, 5, 0, 0},
2008 {-1, 18, 5, 5, 5, 0, 0},
2009 /* test cpMin < 0 && cpMax < 0 */
2010 {-1, -1, 17, 17, 17, 0, 0},
2011 {-4, -5, 17, 17, 17, 0, 0},
2012 /* test cMin >=0 && cpMax < 0 (bug 6814) */
2013 {0, -1, 18, 0, 18, 0, 1},
2014 {17, -5, 18, 17, 18, 0, 1},
2015 {18, -3, 17, 17, 17, 0, 0},
2016 /* test if cpMin > cpMax */
2017 {15, 19, 18, 15, 18, 0, 1},
2018 {19, 15, 18, 15, 18, 0, 1}
2021 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
2026 cr.cpMin = setsel->min;
2027 cr.cpMax = setsel->max;
2028 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
2030 if (setsel->_exsetsel_todo_wine) {
2032 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2035 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2038 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
2040 if (setsel->_getsel_todo_wine) {
2042 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);
2045 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);
2049 static void test_EM_EXSETSEL(void)
2051 HWND hwndRichEdit = new_richedit(NULL);
2053 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
2055 /* sending some text to the window */
2056 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2057 /* 01234567890123456*/
2060 for (i = 0; i < num_tests; i++) {
2061 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
2064 DestroyWindow(hwndRichEdit);
2067 static void test_EM_REPLACESEL(int redraw)
2069 HWND hwndRichEdit = new_richedit(NULL);
2070 char buffer[1024] = {0};
2075 /* sending some text to the window */
2076 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2077 /* 01234567890123456*/
2080 /* FIXME add more tests */
2081 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
2082 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
2083 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
2084 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2085 r = strcmp(buffer, "testing");
2086 ok(0 == r, "expected %d, got %d\n", 0, r);
2088 DestroyWindow(hwndRichEdit);
2090 hwndRichEdit = new_richedit(NULL);
2092 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
2093 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
2095 /* Test behavior with carriage returns and newlines */
2096 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2097 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
2098 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
2099 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2100 r = strcmp(buffer, "RichEdit1");
2101 ok(0 == r, "expected %d, got %d\n", 0, r);
2103 getText.codepage = CP_ACP;
2104 getText.flags = GT_DEFAULT;
2105 getText.lpDefaultChar = NULL;
2106 getText.lpUsedDefChar = NULL;
2107 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2108 ok(strcmp(buffer, "RichEdit1") == 0,
2109 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
2111 /* Test number of lines reported after EM_REPLACESEL */
2112 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2113 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2115 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2116 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
2117 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
2118 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2119 r = strcmp(buffer, "RichEdit1\r\n");
2120 ok(0 == r, "expected %d, got %d\n", 0, r);
2122 getText.codepage = CP_ACP;
2123 getText.flags = GT_DEFAULT;
2124 getText.lpDefaultChar = NULL;
2125 getText.lpUsedDefChar = NULL;
2126 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2127 ok(strcmp(buffer, "RichEdit1\r") == 0,
2128 "EM_GETTEXTEX returned incorrect string\n");
2130 /* Test number of lines reported after EM_REPLACESEL */
2131 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2132 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2134 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
2135 EM_REPLACESEL. The general rule seems to be that Win98's riched20
2136 returns the number of characters *inserted* into the control (after
2137 required conversions), but WinXP's riched20 returns the number of
2138 characters interpreted from the original lParam. Wine's builtin riched20
2139 implements the WinXP behavior.
2141 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2142 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
2143 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
2144 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
2146 /* Test number of lines reported after EM_REPLACESEL */
2147 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2148 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2150 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2151 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2152 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2153 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2155 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2156 r = strcmp(buffer, "RichEdit1\r\n");
2157 ok(0 == r, "expected %d, got %d\n", 0, r);
2159 getText.codepage = CP_ACP;
2160 getText.flags = GT_DEFAULT;
2161 getText.lpDefaultChar = NULL;
2162 getText.lpUsedDefChar = NULL;
2163 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2164 ok(strcmp(buffer, "RichEdit1\r") == 0,
2165 "EM_GETTEXTEX returned incorrect string\n");
2167 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2168 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2169 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2170 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2172 /* The following tests show that richedit should handle the special \r\r\n
2173 sequence by turning it into a single space on insertion. However,
2174 EM_REPLACESEL on WinXP returns the number of characters in the original
2178 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2179 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
2180 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
2181 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2182 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2183 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2184 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2186 /* Test the actual string */
2188 getText.codepage = CP_ACP;
2189 getText.flags = GT_DEFAULT;
2190 getText.lpDefaultChar = NULL;
2191 getText.lpUsedDefChar = NULL;
2192 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2193 ok(strcmp(buffer, "\r\r") == 0,
2194 "EM_GETTEXTEX returned incorrect string\n");
2196 /* Test number of lines reported after EM_REPLACESEL */
2197 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2198 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2200 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2201 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
2202 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
2203 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
2204 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2205 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2206 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
2207 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
2209 /* Test the actual string */
2211 getText.codepage = CP_ACP;
2212 getText.flags = GT_DEFAULT;
2213 getText.lpDefaultChar = NULL;
2214 getText.lpUsedDefChar = NULL;
2215 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2216 ok(strcmp(buffer, " ") == 0,
2217 "EM_GETTEXTEX returned incorrect string\n");
2219 /* Test number of lines reported after EM_REPLACESEL */
2220 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2221 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2223 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2224 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
2225 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2226 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2227 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2228 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2229 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2230 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2232 /* Test the actual string */
2234 getText.codepage = CP_ACP;
2235 getText.flags = GT_DEFAULT;
2236 getText.lpDefaultChar = NULL;
2237 getText.lpUsedDefChar = NULL;
2238 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2239 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
2240 "EM_GETTEXTEX returned incorrect string\n");
2242 /* Test number of lines reported after EM_REPLACESEL */
2243 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2244 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2246 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2247 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
2248 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
2249 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
2250 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2251 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2252 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2253 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2255 /* Test the actual string */
2257 getText.codepage = CP_ACP;
2258 getText.flags = GT_DEFAULT;
2259 getText.lpDefaultChar = NULL;
2260 getText.lpUsedDefChar = NULL;
2261 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2262 ok(strcmp(buffer, " \r") == 0,
2263 "EM_GETTEXTEX returned incorrect string\n");
2265 /* Test number of lines reported after EM_REPLACESEL */
2266 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2267 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2269 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2270 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
2271 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
2272 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
2273 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2274 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2275 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
2276 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
2278 /* Test the actual string */
2280 getText.codepage = CP_ACP;
2281 getText.flags = GT_DEFAULT;
2282 getText.lpDefaultChar = NULL;
2283 getText.lpUsedDefChar = NULL;
2284 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2285 ok(strcmp(buffer, " \r\r") == 0,
2286 "EM_GETTEXTEX returned incorrect string\n");
2288 /* Test number of lines reported after EM_REPLACESEL */
2289 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2290 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2292 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2293 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
2294 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
2295 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
2296 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2297 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2298 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
2299 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
2301 /* Test the actual string */
2303 getText.codepage = CP_ACP;
2304 getText.flags = GT_DEFAULT;
2305 getText.lpDefaultChar = NULL;
2306 getText.lpUsedDefChar = NULL;
2307 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2308 ok(strcmp(buffer, "\rX\r\r\r") == 0,
2309 "EM_GETTEXTEX returned incorrect string\n");
2311 /* Test number of lines reported after EM_REPLACESEL */
2312 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2313 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
2315 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2316 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
2317 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
2318 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2319 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2320 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2321 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2323 /* Test the actual string */
2325 getText.codepage = CP_ACP;
2326 getText.flags = GT_DEFAULT;
2327 getText.lpDefaultChar = NULL;
2328 getText.lpUsedDefChar = NULL;
2329 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2330 ok(strcmp(buffer, "\r\r") == 0,
2331 "EM_GETTEXTEX returned incorrect string\n");
2333 /* Test number of lines reported after EM_REPLACESEL */
2334 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2335 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2337 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2338 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
2339 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2340 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2341 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2342 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2343 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2344 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2346 /* Test the actual string */
2348 getText.codepage = CP_ACP;
2349 getText.flags = GT_DEFAULT;
2350 getText.lpDefaultChar = NULL;
2351 getText.lpUsedDefChar = NULL;
2352 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2353 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
2354 "EM_GETTEXTEX returned incorrect string\n");
2356 /* Test number of lines reported after EM_REPLACESEL */
2357 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2358 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2360 DestroyWindow(hwndRichEdit);
2363 static void test_WM_PASTE(void)
2367 char buffer[1024] = {0};
2368 char key_info[][3] =
2370 /* VirtualKey, ScanCode, WM_CHAR code */
2371 {'C', 0x2e, 3}, /* Ctrl-C */
2372 {'X', 0x2d, 24}, /* Ctrl-X */
2373 {'V', 0x2f, 22}, /* Ctrl-V */
2374 {'Z', 0x2c, 26}, /* Ctrl-Z */
2375 {'Y', 0x15, 25}, /* Ctrl-Y */
2377 const char* text1 = "testing paste\r";
2378 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
2379 const char* text1_after = "testing paste\r\n";
2380 const char* text2 = "testing paste\r\rtesting paste";
2381 const char* text2_after = "testing paste\r\n\r\ntesting paste";
2382 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
2383 HWND hwndRichEdit = new_richedit(NULL);
2385 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
2386 messages, probably because it inspects the keyboard state itself.
2387 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
2389 #define SEND_CTRL_KEY(hwnd, k) \
2390 keybd_event(VK_CONTROL, 0x1d, 0, 0);\
2391 keybd_event(k[0], k[1], 0, 0);\
2392 keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
2393 keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
2394 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
2395 TranslateMessage(&msg); \
2396 DispatchMessage(&msg); \
2399 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
2400 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
2401 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
2402 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
2403 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
2405 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
2406 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
2408 SEND_CTRL_C(hwndRichEdit) /* Copy */
2409 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2410 SEND_CTRL_V(hwndRichEdit) /* Paste */
2411 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2412 /* Pasted text should be visible at this step */
2413 result = strcmp(text1_step1, buffer);
2415 "test paste: strcmp = %i\n", result);
2416 SEND_CTRL_Z(hwndRichEdit) /* Undo */
2417 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2418 /* Text should be the same as before (except for \r -> \r\n conversion) */
2419 result = strcmp(text1_after, buffer);
2421 "test paste: strcmp = %i\n", result);
2423 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
2424 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
2425 SEND_CTRL_C(hwndRichEdit) /* Copy */
2426 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2427 SEND_CTRL_V(hwndRichEdit) /* Paste */
2428 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2429 /* Pasted text should be visible at this step */
2430 result = strcmp(text3, buffer);
2432 "test paste: strcmp = %i\n", result);
2433 SEND_CTRL_Z(hwndRichEdit) /* Undo */
2434 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2435 /* Text should be the same as before (except for \r -> \r\n conversion) */
2436 result = strcmp(text2_after, buffer);
2438 "test paste: strcmp = %i\n", result);
2439 SEND_CTRL_Y(hwndRichEdit) /* Redo */
2440 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2441 /* Text should revert to post-paste state */
2442 result = strcmp(buffer,text3);
2444 "test paste: strcmp = %i\n", result);
2446 DestroyWindow(hwndRichEdit);
2449 static void test_EM_FORMATRANGE(void)
2454 HWND hwndRichEdit = new_richedit(NULL);
2456 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
2458 hdc = GetDC(hwndRichEdit);
2459 ok(hdc != NULL, "Could not get HDC\n");
2461 fr.hdc = fr.hdcTarget = hdc;
2462 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
2463 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
2464 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
2468 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2470 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2473 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2475 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
2481 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2483 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
2486 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2488 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2491 DestroyWindow(hwndRichEdit);
2494 static int nCallbackCount = 0;
2496 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
2499 const char text[] = {'t','e','s','t'};
2501 if (sizeof(text) <= cb)
2503 if ((int)dwCookie != nCallbackCount)
2509 memcpy (pbBuff, text, sizeof(text));
2510 *pcb = sizeof(text);
2517 return 1; /* indicates callback failed */
2520 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
2525 const char** str = (const char**)dwCookie;
2526 int size = strlen(*str);
2532 memcpy(pbBuff, *str, *pcb);
2539 static void test_EM_STREAMIN(void)
2541 HWND hwndRichEdit = new_richedit(NULL);
2544 char buffer[1024] = {0};
2546 const char * streamText0 = "{\\rtf1 TestSomeText}";
2547 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
2548 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
2550 const char * streamText1 =
2551 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
2552 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
2555 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
2556 const char * streamText2 =
2557 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
2558 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
2559 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
2560 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
2561 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
2562 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
2563 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
2565 const char * streamText3 = "RichEdit1";
2567 /* Minimal test without \par at the end */
2568 es.dwCookie = (DWORD_PTR)&streamText0;
2570 es.pfnCallback = test_EM_STREAMIN_esCallback;
2571 SendMessage(hwndRichEdit, EM_STREAMIN,
2572 (WPARAM)(SF_RTF), (LPARAM)&es);
2574 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2576 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
2577 result = strcmp (buffer,"TestSomeText");
2579 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
2580 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
2582 /* Native richedit 2.0 ignores last \par */
2583 es.dwCookie = (DWORD_PTR)&streamText0a;
2585 es.pfnCallback = test_EM_STREAMIN_esCallback;
2586 SendMessage(hwndRichEdit, EM_STREAMIN,
2587 (WPARAM)(SF_RTF), (LPARAM)&es);
2589 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2591 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
2592 result = strcmp (buffer,"TestSomeText");
2594 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
2595 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
2597 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
2598 es.dwCookie = (DWORD_PTR)&streamText0b;
2600 es.pfnCallback = test_EM_STREAMIN_esCallback;
2601 SendMessage(hwndRichEdit, EM_STREAMIN,
2602 (WPARAM)(SF_RTF), (LPARAM)&es);
2604 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2606 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
2607 result = strcmp (buffer,"TestSomeText\r\n");
2609 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
2610 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
2612 es.dwCookie = (DWORD_PTR)&streamText1;
2614 es.pfnCallback = test_EM_STREAMIN_esCallback;
2615 SendMessage(hwndRichEdit, EM_STREAMIN,
2616 (WPARAM)(SF_RTF), (LPARAM)&es);
2618 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2620 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
2621 result = strcmp (buffer,"TestSomeText");
2623 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2624 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
2626 es.dwCookie = (DWORD_PTR)&streamText2;
2628 SendMessage(hwndRichEdit, EM_STREAMIN,
2629 (WPARAM)(SF_RTF), (LPARAM)&es);
2631 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2633 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
2634 ok (strlen(buffer) == 0,
2635 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2636 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
2638 es.dwCookie = (DWORD_PTR)&streamText3;
2640 SendMessage(hwndRichEdit, EM_STREAMIN,
2641 (WPARAM)(SF_RTF), (LPARAM)&es);
2643 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2645 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
2646 ok (strlen(buffer) == 0,
2647 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
2648 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
2650 DestroyWindow(hwndRichEdit);
2653 static void test_EM_StreamIn_Undo(void)
2655 /* The purpose of this test is to determine when a EM_StreamIn should be
2656 * undoable. This is important because WM_PASTE currently uses StreamIn and
2657 * pasting should always be undoable but streaming isn't always.
2660 * StreamIn plain text without SFF_SELECTION.
2661 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
2662 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
2663 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
2664 * Feel free to add tests for other text modes or StreamIn things.
2668 HWND hwndRichEdit = new_richedit(NULL);
2671 char buffer[1024] = {0};
2672 const char randomtext[] = "Some text";
2674 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
2676 /* StreamIn, no SFF_SELECTION */
2677 es.dwCookie = nCallbackCount;
2678 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2679 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2680 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2681 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
2682 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2683 result = strcmp (buffer,"test");
2685 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2687 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2688 ok (result == FALSE,
2689 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
2691 /* StreamIn, SFF_SELECTION, but nothing selected */
2692 es.dwCookie = nCallbackCount;
2693 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2694 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2695 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2696 SendMessage(hwndRichEdit, EM_STREAMIN,
2697 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2698 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2699 result = strcmp (buffer,"testSome text");
2701 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2703 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2705 "EM_STREAMIN with SFF_SELECTION but no selection set "
2706 "should create an undo\n");
2708 /* StreamIn, SFF_SELECTION, with a selection */
2709 es.dwCookie = nCallbackCount;
2710 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2711 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2712 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
2713 SendMessage(hwndRichEdit, EM_STREAMIN,
2714 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2715 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2716 result = strcmp (buffer,"Sometesttext");
2718 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2720 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2722 "EM_STREAMIN with SFF_SELECTION and selection set "
2723 "should create an undo\n");
2727 static BOOL is_em_settextex_supported(HWND hwnd)
2729 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
2730 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
2733 static void test_unicode_conversions(void)
2735 static const WCHAR tW[] = {'t',0};
2736 static const WCHAR teW[] = {'t','e',0};
2737 static const WCHAR textW[] = {'t','e','s','t',0};
2738 static const char textA[] = "test";
2742 int is_win9x, em_settextex_supported, ret;
2744 is_win9x = GetVersion() & 0x80000000;
2746 #define set_textA(hwnd, wm_set_text, txt) \
2748 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
2749 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2750 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2751 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2752 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
2754 #define expect_textA(hwnd, wm_get_text, txt) \
2756 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2757 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2758 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2759 memset(bufA, 0xAA, sizeof(bufA)); \
2760 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2761 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2762 ret = lstrcmpA(bufA, txt); \
2763 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
2766 #define set_textW(hwnd, wm_set_text, txt) \
2768 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
2769 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2770 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2771 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2772 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
2774 #define expect_textW(hwnd, wm_get_text, txt) \
2776 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
2777 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2778 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2779 memset(bufW, 0xAA, sizeof(bufW)); \
2782 assert(wm_get_text == EM_GETTEXTEX); \
2783 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2784 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2788 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2789 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
2791 ret = lstrcmpW(bufW, txt); \
2792 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
2794 #define expect_empty(hwnd, wm_get_text) \
2796 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2797 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2798 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2799 memset(bufA, 0xAA, sizeof(bufA)); \
2800 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2801 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
2802 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
2805 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2806 0, 0, 200, 60, 0, 0, 0, 0);
2807 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2809 ret = IsWindowUnicode(hwnd);
2811 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
2813 ok(ret, "RichEdit20W should be unicode under NT\n");
2815 /* EM_SETTEXTEX is supported starting from version 3.0 */
2816 em_settextex_supported = is_em_settextex_supported(hwnd);
2817 trace("EM_SETTEXTEX is %ssupported on this platform\n",
2818 em_settextex_supported ? "" : "NOT ");
2820 expect_empty(hwnd, WM_GETTEXT);
2821 expect_empty(hwnd, EM_GETTEXTEX);
2823 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
2824 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2825 expect_textA(hwnd, WM_GETTEXT, "t");
2826 expect_textA(hwnd, EM_GETTEXTEX, "t");
2827 expect_textW(hwnd, EM_GETTEXTEX, tW);
2829 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
2830 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2831 expect_textA(hwnd, WM_GETTEXT, "te");
2832 expect_textA(hwnd, EM_GETTEXTEX, "te");
2833 expect_textW(hwnd, EM_GETTEXTEX, teW);
2835 set_textA(hwnd, WM_SETTEXT, NULL);
2836 expect_empty(hwnd, WM_GETTEXT);
2837 expect_empty(hwnd, EM_GETTEXTEX);
2840 set_textA(hwnd, WM_SETTEXT, textW);
2842 set_textA(hwnd, WM_SETTEXT, textA);
2843 expect_textA(hwnd, WM_GETTEXT, textA);
2844 expect_textA(hwnd, EM_GETTEXTEX, textA);
2845 expect_textW(hwnd, EM_GETTEXTEX, textW);
2847 if (em_settextex_supported)
2849 set_textA(hwnd, EM_SETTEXTEX, textA);
2850 expect_textA(hwnd, WM_GETTEXT, textA);
2851 expect_textA(hwnd, EM_GETTEXTEX, textA);
2852 expect_textW(hwnd, EM_GETTEXTEX, textW);
2857 set_textW(hwnd, WM_SETTEXT, textW);
2858 expect_textW(hwnd, WM_GETTEXT, textW);
2859 expect_textA(hwnd, WM_GETTEXT, textA);
2860 expect_textW(hwnd, EM_GETTEXTEX, textW);
2861 expect_textA(hwnd, EM_GETTEXTEX, textA);
2863 if (em_settextex_supported)
2865 set_textW(hwnd, EM_SETTEXTEX, textW);
2866 expect_textW(hwnd, WM_GETTEXT, textW);
2867 expect_textA(hwnd, WM_GETTEXT, textA);
2868 expect_textW(hwnd, EM_GETTEXTEX, textW);
2869 expect_textA(hwnd, EM_GETTEXTEX, textA);
2872 DestroyWindow(hwnd);
2874 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
2875 0, 0, 200, 60, 0, 0, 0, 0);
2876 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2878 ret = IsWindowUnicode(hwnd);
2879 ok(!ret, "RichEdit20A should NOT be unicode\n");
2881 set_textA(hwnd, WM_SETTEXT, textA);
2882 expect_textA(hwnd, WM_GETTEXT, textA);
2883 expect_textA(hwnd, EM_GETTEXTEX, textA);
2884 expect_textW(hwnd, EM_GETTEXTEX, textW);
2886 if (em_settextex_supported)
2888 set_textA(hwnd, EM_SETTEXTEX, textA);
2889 expect_textA(hwnd, WM_GETTEXT, textA);
2890 expect_textA(hwnd, EM_GETTEXTEX, textA);
2891 expect_textW(hwnd, EM_GETTEXTEX, textW);
2896 set_textW(hwnd, WM_SETTEXT, textW);
2897 expect_textW(hwnd, WM_GETTEXT, textW);
2898 expect_textA(hwnd, WM_GETTEXT, textA);
2899 expect_textW(hwnd, EM_GETTEXTEX, textW);
2900 expect_textA(hwnd, EM_GETTEXTEX, textA);
2902 if (em_settextex_supported)
2904 set_textW(hwnd, EM_SETTEXTEX, textW);
2905 expect_textW(hwnd, WM_GETTEXT, textW);
2906 expect_textA(hwnd, WM_GETTEXT, textA);
2907 expect_textW(hwnd, EM_GETTEXTEX, textW);
2908 expect_textA(hwnd, EM_GETTEXTEX, textA);
2911 DestroyWindow(hwnd);
2914 static void test_WM_CHAR(void)
2918 const char * char_list = "abc\rabc\r";
2919 const char * expected_content_single = "abcabc";
2920 const char * expected_content_multi = "abc\r\nabc\r\n";
2921 char buffer[64] = {0};
2924 /* single-line control must IGNORE carriage returns */
2925 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2926 0, 0, 200, 60, 0, 0, 0, 0);
2927 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2930 while (*p != '\0') {
2931 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2932 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2933 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2934 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2938 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2939 ret = strcmp(buffer, expected_content_single);
2940 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2942 DestroyWindow(hwnd);
2944 /* multi-line control inserts CR normally */
2945 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
2946 0, 0, 200, 60, 0, 0, 0, 0);
2947 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2950 while (*p != '\0') {
2951 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2952 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2953 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2954 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2958 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2959 ret = strcmp(buffer, expected_content_multi);
2960 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2962 DestroyWindow(hwnd);
2965 static void test_EM_GETTEXTLENGTHEX(void)
2968 GETTEXTLENGTHEX gtl;
2970 const char * base_string = "base string";
2971 const char * test_string = "a\nb\n\n\r\n";
2972 const char * test_string_after = "a";
2973 const char * test_string_2 = "a\rtest\rstring";
2974 char buffer[64] = {0};
2977 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2978 0, 0, 200, 60, 0, 0, 0, 0);
2979 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2981 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2982 gtl.codepage = CP_ACP;
2983 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2984 ok(ret == 0, "ret %d\n",ret);
2986 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2987 gtl.codepage = CP_ACP;
2988 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2989 ok(ret == 0, "ret %d\n",ret);
2991 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
2993 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2994 gtl.codepage = CP_ACP;
2995 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2996 ok(ret == strlen(base_string), "ret %d\n",ret);
2998 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2999 gtl.codepage = CP_ACP;
3000 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3001 ok(ret == strlen(base_string), "ret %d\n",ret);
3003 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3005 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3006 gtl.codepage = CP_ACP;
3007 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3008 ok(ret == 1, "ret %d\n",ret);
3010 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3011 gtl.codepage = CP_ACP;
3012 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3013 ok(ret == 1, "ret %d\n",ret);
3015 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3016 ret = strcmp(buffer, test_string_after);
3017 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3019 DestroyWindow(hwnd);
3022 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
3023 0, 0, 200, 60, 0, 0, 0, 0);
3024 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3026 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3027 gtl.codepage = CP_ACP;
3028 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3029 ok(ret == 0, "ret %d\n",ret);
3031 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3032 gtl.codepage = CP_ACP;
3033 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3034 ok(ret == 0, "ret %d\n",ret);
3036 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
3038 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3039 gtl.codepage = CP_ACP;
3040 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3041 ok(ret == strlen(base_string), "ret %d\n",ret);
3043 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3044 gtl.codepage = CP_ACP;
3045 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3046 ok(ret == strlen(base_string), "ret %d\n",ret);
3048 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
3050 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3051 gtl.codepage = CP_ACP;
3052 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3053 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
3055 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3056 gtl.codepage = CP_ACP;
3057 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3058 ok(ret == strlen(test_string_2), "ret %d\n",ret);
3060 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3062 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3063 gtl.codepage = CP_ACP;
3064 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3065 ok(ret == 10, "ret %d\n",ret);
3067 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3068 gtl.codepage = CP_ACP;
3069 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3070 ok(ret == 6, "ret %d\n",ret);
3072 DestroyWindow(hwnd);
3076 /* globals that parent and child access when checking event masks & notifications */
3077 static HWND eventMaskEditHwnd = 0;
3078 static int queriedEventMask;
3079 static int watchForEventMask = 0;
3081 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
3082 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3084 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
3086 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3088 return DefWindowProcA(hwnd, message, wParam, lParam);
3091 /* test event masks in combination with WM_COMMAND */
3092 static void test_eventMask(void)
3097 const char text[] = "foo bar\n";
3100 /* register class to capture WM_COMMAND */
3102 cls.lpfnWndProc = ParentMsgCheckProcA;
3105 cls.hInstance = GetModuleHandleA(0);
3107 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3108 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3109 cls.lpszMenuName = NULL;
3110 cls.lpszClassName = "EventMaskParentClass";
3111 if(!RegisterClassA(&cls)) assert(0);
3113 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3114 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3115 ok (parent != 0, "Failed to create parent window\n");
3117 eventMaskEditHwnd = new_richedit(parent);
3118 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
3120 eventMask = ENM_CHANGE | ENM_UPDATE;
3121 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
3122 ok(ret == ENM_NONE, "wrong event mask\n");
3123 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3124 ok(ret == eventMask, "failed to set event mask\n");
3126 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
3127 queriedEventMask = 0; /* initialize to something other than we expect */
3128 watchForEventMask = EN_CHANGE;
3129 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
3130 ok(ret == TRUE, "failed to set text\n");
3131 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
3132 notification in response to WM_SETTEXT */
3133 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
3134 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
3138 static int received_WM_NOTIFY = 0;
3139 static int modify_at_WM_NOTIFY = 0;
3140 static HWND hwndRichedit_WM_NOTIFY;
3142 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3144 if(message == WM_NOTIFY)
3146 received_WM_NOTIFY = 1;
3147 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
3149 return DefWindowProcA(hwnd, message, wParam, lParam);
3152 static void test_WM_NOTIFY(void)
3158 /* register class to capture WM_NOTIFY */
3160 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
3163 cls.hInstance = GetModuleHandleA(0);
3165 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3166 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3167 cls.lpszMenuName = NULL;
3168 cls.lpszClassName = "WM_NOTIFY_ParentClass";
3169 if(!RegisterClassA(&cls)) assert(0);
3171 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3172 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3173 ok (parent != 0, "Failed to create parent window\n");
3175 hwndRichedit_WM_NOTIFY = new_richedit(parent);
3176 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
3178 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
3180 /* Notifications for selection change should only be sent when selection
3181 actually changes. EM_SETCHARFORMAT is one message that calls
3182 ME_CommitUndo, which should check whether message should be sent */
3183 received_WM_NOTIFY = 0;
3184 cf2.cbSize = sizeof(CHARFORMAT2);
3185 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3187 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3188 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3189 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
3190 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3192 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
3194 received_WM_NOTIFY = 0;
3195 modify_at_WM_NOTIFY = 0;
3196 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3197 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3198 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3200 received_WM_NOTIFY = 0;
3201 modify_at_WM_NOTIFY = 0;
3202 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
3203 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3205 received_WM_NOTIFY = 0;
3206 modify_at_WM_NOTIFY = 0;
3207 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3208 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3209 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3211 DestroyWindow(hwndRichedit_WM_NOTIFY);
3212 DestroyWindow(parent);
3215 START_TEST( editor )
3220 /* Must explicitly LoadLibrary(). The test has no references to functions in
3221 * RICHED20.DLL, so the linker doesn't actually link to it. */
3222 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
3223 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
3227 test_EM_SCROLLCARET();
3230 test_EM_LINELENGTH();
3231 test_EM_SETCHARFORMAT();
3232 test_EM_SETTEXTMODE();
3233 test_TM_PLAINTEXT();
3234 test_EM_SETOPTIONS();
3236 test_EM_GETTEXTRANGE();
3237 test_EM_GETSELTEXT();
3238 test_EM_AUTOURLDETECT();
3239 test_EM_SETUNDOLIMIT();
3241 test_EM_SETTEXTEX();
3242 test_EM_LIMITTEXT();
3243 test_EM_EXLIMITTEXT();
3244 test_EM_GETLIMITTEXT();
3246 test_EM_GETMODIFY();
3250 test_EM_STREAMOUT();
3251 test_EM_StreamIn_Undo();
3252 test_EM_FORMATRANGE();
3253 test_unicode_conversions();
3254 test_EM_GETTEXTLENGTHEX();
3255 test_EM_REPLACESEL(1);
3256 test_EM_REPLACESEL(0);
3260 /* Set the environment variable WINETEST_RICHED20 to keep windows
3261 * responsive and open for 30 seconds. This is useful for debugging.
3263 * The message pump uses PeekMessage() to empty the queue and then sleeps for
3264 * 50ms before retrying the queue. */
3265 end = time(NULL) + 30;
3266 if (getenv( "WINETEST_RICHED20" )) {
3267 while (time(NULL) < end) {
3268 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
3269 TranslateMessage(&msg);
3270 DispatchMessage(&msg);
3277 OleFlushClipboard();
3278 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());