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\n", result);
833 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
835 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
837 textRange.lpstrText = buffer;
838 textRange.chrg.cpMin = 4;
839 textRange.chrg.cpMax = 11;
840 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
841 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
842 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
844 DestroyWindow(hwndRichEdit);
847 static void test_EM_GETSELTEXT(void)
849 HWND hwndRichEdit = new_richedit(NULL);
850 const char * text1 = "foo bar\r\nfoo bar";
851 const char * text2 = "foo bar\rfoo bar";
852 const char * expect = "bar\rfoo";
853 char buffer[1024] = {0};
856 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
858 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
859 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
860 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
861 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
863 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
865 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
866 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
867 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
868 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
870 DestroyWindow(hwndRichEdit);
873 /* FIXME: need to test unimplemented options and robustly test wparam */
874 static void test_EM_SETOPTIONS(void)
876 HWND hwndRichEdit = new_richedit(NULL);
877 static const char text[] = "Hello. My name is RichEdit!";
878 char buffer[1024] = {0};
880 /* NEGATIVE TESTING - NO OPTIONS SET */
881 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
882 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
884 /* testing no readonly by sending 'a' to the control*/
885 SetFocus(hwndRichEdit);
886 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
887 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
889 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
890 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
892 /* READONLY - sending 'a' to the control */
893 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
894 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
895 SetFocus(hwndRichEdit);
896 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
897 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
898 ok(buffer[0]==text[0],
899 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
901 DestroyWindow(hwndRichEdit);
904 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
906 CHARFORMAT2W text_format;
907 int link_present = 0;
908 text_format.cbSize = sizeof(text_format);
909 SendMessage(hwnd, EM_SETSEL, 0, 1);
910 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
911 link_present = text_format.dwEffects & CFE_LINK;
913 { /* control text is url; should get CFE_LINK */
914 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
918 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
922 static HWND new_static_wnd(HWND parent) {
923 return new_window("Static", 0, parent);
926 static void test_EM_AUTOURLDETECT(void)
933 {"http://www.winehq.org", 1},
934 {"http//winehq.org", 0},
935 {"ww.winehq.org", 0},
936 {"www.winehq.org", 1},
937 {"ftp://192.168.1.1", 1},
938 {"ftp//192.168.1.1", 0},
939 {"mailto:your@email.com", 1},
940 {"prospero:prosperoserver", 1},
942 {"news:newserver", 1},
943 {"wais:waisserver", 1}
948 HWND hwndRichEdit, parent;
950 parent = new_static_wnd(NULL);
951 hwndRichEdit = new_richedit(parent);
952 /* Try and pass EM_AUTOURLDETECT some test wParam values */
953 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
954 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
955 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
956 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
957 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
958 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
959 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
960 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
961 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
962 /* for each url, check the text to see if CFE_LINK effect is present */
963 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
964 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
965 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
966 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
967 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
968 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
969 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
970 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
971 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
973 DestroyWindow(hwndRichEdit);
974 DestroyWindow(parent);
977 static void test_EM_SCROLL(void)
980 int r; /* return value */
981 int expr; /* expected return value */
982 HWND hwndRichEdit = new_richedit(NULL);
983 int y_before, y_after; /* units of lines of text */
985 /* test a richedit box containing a single line of text */
986 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
988 for (i = 0; i < 4; i++) {
989 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
991 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
992 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
993 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
994 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
995 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
996 "(i == %d)\n", y_after, i);
1000 * test a richedit box that will scroll. There are two general
1001 * cases: the case without any long lines and the case with a long
1004 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
1006 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
1008 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
1009 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1010 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1011 "LONG LINE \nb\nc\nd\ne");
1012 for (j = 0; j < 12; j++) /* reset scroll position to top */
1013 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
1015 /* get first visible line */
1016 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1017 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
1019 /* get new current first visible line */
1020 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1022 ok(((r & 0xffffff00) == 0x00010000) &&
1023 ((r & 0x000000ff) != 0x00000000),
1024 "EM_SCROLL page down didn't scroll by a small positive number of "
1025 "lines (r == 0x%08x)\n", r);
1026 ok(y_after > y_before, "EM_SCROLL page down not functioning "
1027 "(line %d scrolled to line %d\n", y_before, y_after);
1031 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
1032 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1033 ok(((r & 0xffffff00) == 0x0001ff00),
1034 "EM_SCROLL page up didn't scroll by a small negative number of lines "
1035 "(r == 0x%08x)\n", r);
1036 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
1037 "%d scrolled to line %d\n", y_before, y_after);
1041 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
1043 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1045 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
1046 "(r == 0x%08x)\n", r);
1047 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
1048 "1 line (%d scrolled to %d)\n", y_before, y_after);
1052 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
1054 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1056 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
1057 "(r == 0x%08x)\n", r);
1058 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
1059 "line (%d scrolled to %d)\n", y_before, y_after);
1063 r = SendMessage(hwndRichEdit, EM_SCROLL,
1064 SB_LINEUP, 0); /* lineup beyond top */
1066 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1069 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
1070 ok(y_before == y_after,
1071 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
1075 r = SendMessage(hwndRichEdit, EM_SCROLL,
1076 SB_PAGEUP, 0);/*page up beyond top */
1078 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1081 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
1082 ok(y_before == y_after,
1083 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
1085 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
1086 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
1087 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1088 r = SendMessage(hwndRichEdit, EM_SCROLL,
1089 SB_PAGEDOWN, 0); /* page down beyond bot */
1090 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1093 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
1094 ok(y_before == y_after,
1095 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
1098 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1099 SendMessage(hwndRichEdit, EM_SCROLL,
1100 SB_LINEDOWN, 0); /* line down beyond bot */
1101 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1104 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
1105 ok(y_before == y_after,
1106 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
1109 DestroyWindow(hwndRichEdit);
1112 static void test_EM_SETUNDOLIMIT(void)
1114 /* cases we test for:
1115 * default behaviour - limiting at 100 undo's
1116 * undo disabled - setting a limit of 0
1117 * undo limited - undo limit set to some to some number, like 2
1118 * bad input - sending a negative number should default to 100 undo's */
1120 HWND hwndRichEdit = new_richedit(NULL);
1125 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1128 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1129 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
1130 also, multiple pastes don't combine like WM_CHAR would */
1131 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1133 /* first case - check the default */
1134 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1135 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
1136 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1137 for (i=0; i<100; i++) /* Undo 100 of them */
1138 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1139 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1140 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
1142 /* second case - cannot undo */
1143 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
1144 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
1145 SendMessage(hwndRichEdit,
1146 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
1147 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1148 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
1150 /* third case - set it to an arbitrary number */
1151 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
1152 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
1153 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1154 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1155 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1156 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
1157 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
1158 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
1159 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1160 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1161 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
1162 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1163 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1164 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
1166 /* fourth case - setting negative numbers should default to 100 undos */
1167 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1168 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
1170 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
1172 DestroyWindow(hwndRichEdit);
1175 static void test_ES_PASSWORD(void)
1177 /* This isn't hugely testable, so we're just going to run it through its paces */
1179 HWND hwndRichEdit = new_richedit(NULL);
1182 /* First, check the default of a regular control */
1183 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1185 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
1187 /* Now, set it to something normal */
1188 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
1189 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1191 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1193 /* Now, set it to something odd */
1194 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
1195 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1197 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1198 DestroyWindow(hwndRichEdit);
1201 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
1206 char** str = (char**)dwCookie;
1209 memcpy(*str, pbBuff, *pcb);
1215 static void test_WM_SETTEXT()
1217 HWND hwndRichEdit = new_richedit(NULL);
1218 const char * TestItem1 = "TestSomeText";
1219 const char * TestItem2 = "TestSomeText\r";
1220 const char * TestItem2_after = "TestSomeText\r\n";
1221 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
1222 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
1223 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
1224 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
1225 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
1226 const char * TestItem5_after = "TestSomeText TestSomeText";
1227 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
1228 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
1229 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
1230 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
1232 char buf[1024] = {0};
1237 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1238 any solitary \r to be converted to \r\n on return. Properly paired
1239 \r\n are not affected. It also shows that the special sequence \r\r\n
1240 gets converted to a single space.
1243 #define TEST_SETTEXT(a, b) \
1244 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
1245 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
1246 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
1247 ok (result == lstrlen(buf), \
1248 "WM_GETTEXT returned %ld instead of expected %u\n", \
1249 result, lstrlen(buf)); \
1250 result = strcmp(b, buf); \
1252 "WM_SETTEXT round trip: strcmp = %ld\n", result);
1254 TEST_SETTEXT(TestItem1, TestItem1)
1255 TEST_SETTEXT(TestItem2, TestItem2_after)
1256 TEST_SETTEXT(TestItem3, TestItem3_after)
1257 TEST_SETTEXT(TestItem3_after, TestItem3_after)
1258 TEST_SETTEXT(TestItem4, TestItem4_after)
1259 TEST_SETTEXT(TestItem5, TestItem5_after)
1260 TEST_SETTEXT(TestItem6, TestItem6_after)
1261 TEST_SETTEXT(TestItem7, TestItem7_after)
1263 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
1264 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1266 es.dwCookie = (DWORD_PTR)&p;
1268 es.pfnCallback = test_WM_SETTEXT_esCallback;
1269 memset(buf, 0, sizeof(buf));
1270 SendMessage(hwndRichEdit, EM_STREAMOUT,
1271 (WPARAM)(SF_RTF), (LPARAM)&es);
1272 trace("EM_STREAMOUT produced: \n%s\n", buf);
1273 TEST_SETTEXT(buf, TestItem1)
1276 DestroyWindow(hwndRichEdit);
1279 static void test_EM_STREAMOUT(void)
1281 HWND hwndRichEdit = new_richedit(NULL);
1284 char buf[1024] = {0};
1287 const char * TestItem1 = "TestSomeText";
1288 const char * TestItem2 = "TestSomeText\r";
1289 const char * TestItem3 = "TestSomeText\r\n";
1291 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1293 es.dwCookie = (DWORD_PTR)&p;
1295 es.pfnCallback = test_WM_SETTEXT_esCallback;
1296 memset(buf, 0, sizeof(buf));
1297 SendMessage(hwndRichEdit, EM_STREAMOUT,
1298 (WPARAM)(SF_TEXT), (LPARAM)&es);
1300 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
1301 ok(strcmp(buf, TestItem1) == 0,
1302 "streamed text different, got %s\n", buf);
1304 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
1306 es.dwCookie = (DWORD_PTR)&p;
1308 es.pfnCallback = test_WM_SETTEXT_esCallback;
1309 memset(buf, 0, sizeof(buf));
1310 SendMessage(hwndRichEdit, EM_STREAMOUT,
1311 (WPARAM)(SF_TEXT), (LPARAM)&es);
1313 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
1314 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
1315 ok(strcmp(buf, TestItem3) == 0,
1316 "streamed text different from, got %s\n", buf);
1317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
1319 es.dwCookie = (DWORD_PTR)&p;
1321 es.pfnCallback = test_WM_SETTEXT_esCallback;
1322 memset(buf, 0, sizeof(buf));
1323 SendMessage(hwndRichEdit, EM_STREAMOUT,
1324 (WPARAM)(SF_TEXT), (LPARAM)&es);
1326 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
1327 ok(strcmp(buf, TestItem3) == 0,
1328 "streamed text different, got %s\n", buf);
1330 DestroyWindow(hwndRichEdit);
1333 static void test_EM_SETTEXTEX(void)
1335 HWND hwndRichEdit = new_richedit(NULL);
1338 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1340 'T', 'e', 'x', 't', 0};
1341 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1345 const char * TestItem2_after = "TestSomeText\r\n";
1346 WCHAR TestItem3[] = {'T', 'e', 's', 't',
1349 '\r','\n','\r','\n', 0};
1350 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
1354 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
1358 WCHAR TestItem4[] = {'T', 'e', 's', 't',
1361 '\r','\r','\n','\r',
1363 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
1367 #define MAX_BUF_LEN 1024
1368 WCHAR buf[MAX_BUF_LEN];
1374 setText.codepage = 1200; /* no constant for unicode */
1375 getText.codepage = 1200; /* no constant for unicode */
1376 getText.cb = MAX_BUF_LEN;
1377 getText.flags = GT_DEFAULT;
1378 getText.lpDefaultChar = NULL;
1379 getText.lpUsedDefChar = NULL;
1382 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1383 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1384 ok(lstrcmpW(buf, TestItem1) == 0,
1385 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1387 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1388 convert \r to \r\n on return
1390 setText.codepage = 1200; /* no constant for unicode */
1391 getText.codepage = 1200; /* no constant for unicode */
1392 getText.cb = MAX_BUF_LEN;
1393 getText.flags = GT_DEFAULT;
1394 getText.lpDefaultChar = NULL;
1395 getText.lpUsedDefChar = NULL;
1397 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1398 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1399 ok(lstrcmpW(buf, TestItem2) == 0,
1400 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1402 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1403 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1404 ok(strcmp((const char *)buf, TestItem2_after) == 0,
1405 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1407 /* Baseline test for just-enough buffer space for string */
1408 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1409 getText.codepage = 1200; /* no constant for unicode */
1410 getText.flags = GT_DEFAULT;
1411 getText.lpDefaultChar = NULL;
1412 getText.lpUsedDefChar = NULL;
1413 memset(buf, 0, MAX_BUF_LEN);
1414 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1415 ok(lstrcmpW(buf, TestItem2) == 0,
1416 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1418 /* When there is enough space for one character, but not both, of the CRLF
1419 pair at the end of the string, the CR is not copied at all. That is,
1420 the caller must not see CRLF pairs truncated to CR at the end of the
1423 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1424 getText.codepage = 1200; /* no constant for unicode */
1425 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
1426 getText.lpDefaultChar = NULL;
1427 getText.lpUsedDefChar = NULL;
1428 memset(buf, 0, MAX_BUF_LEN);
1429 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1430 ok(lstrcmpW(buf, TestItem1) == 0,
1431 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1434 /* \r\n pairs get changed into \r */
1435 setText.codepage = 1200; /* no constant for unicode */
1436 getText.codepage = 1200; /* no constant for unicode */
1437 getText.cb = MAX_BUF_LEN;
1438 getText.flags = GT_DEFAULT;
1439 getText.lpDefaultChar = NULL;
1440 getText.lpUsedDefChar = NULL;
1442 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
1443 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1444 ok(lstrcmpW(buf, TestItem3_after) == 0,
1445 "EM_SETTEXTEX did not convert properly\n");
1447 /* \n also gets changed to \r */
1448 setText.codepage = 1200; /* no constant for unicode */
1449 getText.codepage = 1200; /* no constant for unicode */
1450 getText.cb = MAX_BUF_LEN;
1451 getText.flags = GT_DEFAULT;
1452 getText.lpDefaultChar = NULL;
1453 getText.lpUsedDefChar = NULL;
1455 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
1456 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1457 ok(lstrcmpW(buf, TestItem3_after) == 0,
1458 "EM_SETTEXTEX did not convert properly\n");
1460 /* \r\r\n gets changed into single space */
1461 setText.codepage = 1200; /* no constant for unicode */
1462 getText.codepage = 1200; /* no constant for unicode */
1463 getText.cb = MAX_BUF_LEN;
1464 getText.flags = GT_DEFAULT;
1465 getText.lpDefaultChar = NULL;
1466 getText.lpUsedDefChar = NULL;
1468 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
1469 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1470 ok(lstrcmpW(buf, TestItem4_after) == 0,
1471 "EM_SETTEXTEX did not convert properly\n");
1473 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1474 (WPARAM)&setText, (LPARAM) NULL);
1475 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1478 "EM_SETTEXTEX returned %d, instead of 1\n",result);
1479 ok(lstrlenW(buf) == 0,
1480 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1482 /* put some text back */
1484 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1485 /* select some text */
1488 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1489 /* replace current selection */
1490 setText.flags = ST_SELECTION;
1491 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1492 (WPARAM)&setText, (LPARAM) NULL);
1494 "EM_SETTEXTEX with NULL lParam to replace selection"
1495 " with no text should return 0. Got %i\n",
1498 /* put some text back */
1500 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1501 /* select some text */
1504 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1505 /* replace current selection */
1506 setText.flags = ST_SELECTION;
1507 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1508 (WPARAM)&setText, (LPARAM) TestItem1);
1510 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1511 ok(result == lstrlenW(TestItem1),
1512 "EM_SETTEXTEX with NULL lParam to replace selection"
1513 " with no text should return 0. Got %i\n",
1515 ok(lstrlenW(buf) == 22,
1516 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1519 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
1520 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
1522 es.dwCookie = (DWORD_PTR)&p;
1524 es.pfnCallback = test_WM_SETTEXT_esCallback;
1525 memset(buf, 0, sizeof(buf));
1526 SendMessage(hwndRichEdit, EM_STREAMOUT,
1527 (WPARAM)(SF_RTF), (LPARAM)&es);
1528 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
1530 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
1531 getText.codepage = 1200; /* no constant for unicode */
1532 getText.cb = MAX_BUF_LEN;
1533 getText.flags = GT_DEFAULT;
1534 getText.lpDefaultChar = NULL;
1535 getText.lpUsedDefChar = NULL;
1538 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
1539 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1540 ok(lstrcmpW(buf, TestItem1) == 0,
1541 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1544 DestroyWindow(hwndRichEdit);
1547 static void test_EM_LIMITTEXT(void)
1551 HWND hwndRichEdit = new_richedit(NULL);
1553 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1554 * about setting the length to -1 for multiline edit controls doesn't happen.
1557 /* Don't check default gettextlimit case. That's done in other tests */
1559 /* Set textlimit to 100 */
1560 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1561 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1563 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1565 /* Set textlimit to 0 */
1566 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1567 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1569 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1571 /* Set textlimit to -1 */
1572 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1573 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1575 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1577 /* Set textlimit to -2 */
1578 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1579 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1581 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1583 DestroyWindow (hwndRichEdit);
1587 static void test_EM_EXLIMITTEXT(void)
1589 int i, selBegin, selEnd, len1, len2;
1591 char text[1024 + 1];
1592 char buffer[1024 + 1];
1593 int textlimit = 0; /* multiple of 100 */
1594 HWND hwndRichEdit = new_richedit(NULL);
1596 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1597 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1600 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1601 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1603 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1606 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1607 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1609 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1611 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1612 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1613 /* default for WParam = 0 */
1614 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1616 textlimit = sizeof(text)-1;
1617 memset(text, 'W', textlimit);
1618 text[sizeof(text)-1] = 0;
1619 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1620 /* maxed out text */
1621 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1623 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1624 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1625 len1 = selEnd - selBegin;
1627 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1628 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1629 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1630 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1631 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1632 len2 = selEnd - selBegin;
1635 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1638 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1639 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1640 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1641 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1642 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1643 len1 = selEnd - selBegin;
1646 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1649 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1650 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1651 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1652 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1653 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1654 len2 = selEnd - selBegin;
1657 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1660 /* set text up to the limit, select all the text, then add a char */
1662 memset(text, 'W', textlimit);
1663 text[textlimit] = 0;
1664 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1665 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1666 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1667 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1668 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1669 result = strcmp(buffer, "A");
1670 ok(0 == result, "got string = \"%s\"\n", buffer);
1672 /* WM_SETTEXT not limited */
1674 memset(text, 'W', textlimit);
1675 text[textlimit] = 0;
1676 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1677 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1678 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1680 ok(10 == i, "expected 10 chars\n");
1681 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1682 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1684 /* try inserting more text at end */
1685 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1686 ok(0 == i, "WM_CHAR wasn't processed\n");
1687 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1689 ok(10 == i, "expected 10 chars, got %i\n", i);
1690 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1691 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1693 /* try inserting text at beginning */
1694 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1695 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1696 ok(0 == i, "WM_CHAR wasn't processed\n");
1697 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1699 ok(10 == i, "expected 10 chars, got %i\n", i);
1700 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1701 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1703 /* WM_CHAR is limited */
1705 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1706 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1707 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1708 ok(0 == i, "WM_CHAR wasn't processed\n");
1709 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1710 ok(0 == i, "WM_CHAR wasn't processed\n");
1711 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1713 ok(1 == i, "expected 1 chars, got %i instead\n", i);
1715 DestroyWindow(hwndRichEdit);
1718 static void test_EM_GETLIMITTEXT(void)
1721 HWND hwndRichEdit = new_richedit(NULL);
1723 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1724 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1726 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1727 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1728 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1730 DestroyWindow(hwndRichEdit);
1733 static void test_WM_SETFONT(void)
1735 /* There is no invalid input or error conditions for this function.
1736 * NULL wParam and lParam just fall back to their default values
1737 * It should be noted that even if you use a gibberish name for your fonts
1738 * here, it will still work because the name is stored. They will display as
1739 * System, but will report their name to be whatever they were created as */
1741 HWND hwndRichEdit = new_richedit(NULL);
1742 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1743 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1744 FF_DONTCARE, "Marlett");
1745 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1746 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1747 FF_DONTCARE, "MS Sans Serif");
1748 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1749 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1750 FF_DONTCARE, "Courier");
1751 LOGFONTA sentLogFont;
1752 CHARFORMAT2A returnedCF2A;
1754 returnedCF2A.cbSize = sizeof(returnedCF2A);
1756 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1757 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1758 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1760 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1761 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1762 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1763 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1765 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1766 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1767 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1768 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1769 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1770 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1772 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1773 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1774 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1775 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1776 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1777 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1779 /* This last test is special since we send in NULL. We clear the variables
1780 * and just compare to "System" instead of the sent in font name. */
1781 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1782 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1783 returnedCF2A.cbSize = sizeof(returnedCF2A);
1785 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1786 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1787 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1788 ok (!strcmp("System",returnedCF2A.szFaceName),
1789 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1791 DestroyWindow(hwndRichEdit);
1795 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1800 const char** str = (const char**)dwCookie;
1801 int size = strlen(*str);
1802 if(size > 3) /* let's make it piecemeal for fun */
1809 memcpy(pbBuff, *str, *pcb);
1815 static void test_EM_GETMODIFY(void)
1817 HWND hwndRichEdit = new_richedit(NULL);
1820 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1822 'T', 'e', 'x', 't', 0};
1823 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1825 'O', 't', 'h', 'e', 'r',
1826 'T', 'e', 'x', 't', 0};
1827 const char* streamText = "hello world";
1832 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1833 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1834 FF_DONTCARE, "Courier");
1836 setText.codepage = 1200; /* no constant for unicode */
1837 setText.flags = ST_KEEPUNDO;
1840 /* modify flag shouldn't be set when richedit is first created */
1841 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1843 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1845 /* setting modify flag should actually set it */
1846 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1847 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1849 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1851 /* clearing modify flag should actually clear it */
1852 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1853 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1855 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1857 /* setting font doesn't change modify flag */
1858 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1859 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1860 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1862 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1864 /* setting text should set modify flag */
1865 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1866 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1867 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1869 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1871 /* undo previous text doesn't reset modify flag */
1872 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1873 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1875 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1877 /* set text with no flag to keep undo stack should not set modify flag */
1878 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1880 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1881 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1883 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1885 /* WM_SETTEXT doesn't modify */
1886 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1887 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1888 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1890 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1892 /* clear the text */
1893 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1894 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1895 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1897 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1900 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1901 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1902 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1903 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1904 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1906 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1908 /* copy/paste text 1 */
1909 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1910 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1911 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1912 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1913 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1915 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1917 /* copy/paste text 2 */
1918 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1919 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1920 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1921 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1922 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1923 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1925 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1928 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1929 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1930 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1931 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1933 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1936 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1937 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1938 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1939 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1941 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1943 /* set char format */
1944 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1945 cf2.cbSize = sizeof(CHARFORMAT2);
1946 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1948 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1949 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1950 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1951 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1952 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
1953 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1955 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1957 /* set para format */
1958 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1959 pf2.cbSize = sizeof(PARAFORMAT2);
1960 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1962 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1963 pf2.wAlignment = PFA_RIGHT;
1964 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1965 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1967 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1970 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1971 es.dwCookie = (DWORD_PTR)&streamText;
1973 es.pfnCallback = test_EM_GETMODIFY_esCallback;
1974 SendMessage(hwndRichEdit, EM_STREAMIN,
1975 (WPARAM)(SF_TEXT), (LPARAM)&es);
1976 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1978 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1980 DestroyWindow(hwndRichEdit);
1986 long expected_retval;
1987 int expected_getsel_start;
1988 int expected_getsel_end;
1989 int _exsetsel_todo_wine;
1990 int _getsel_todo_wine;
1993 const struct exsetsel_s exsetsel_tests[] = {
1995 {5, 10, 10, 5, 10, 0, 0},
1996 {15, 17, 17, 15, 17, 0, 0},
1997 /* test cpMax > strlen() */
1998 {0, 100, 18, 0, 18, 0, 1},
1999 /* test cpMin == cpMax */
2000 {5, 5, 5, 5, 5, 0, 0},
2001 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
2002 {-1, 0, 5, 5, 5, 0, 0},
2003 {-1, 17, 5, 5, 5, 0, 0},
2004 {-1, 18, 5, 5, 5, 0, 0},
2005 /* test cpMin < 0 && cpMax < 0 */
2006 {-1, -1, 17, 17, 17, 0, 0},
2007 {-4, -5, 17, 17, 17, 0, 0},
2008 /* test cMin >=0 && cpMax < 0 (bug 6814) */
2009 {0, -1, 18, 0, 18, 0, 1},
2010 {17, -5, 18, 17, 18, 0, 1},
2011 {18, -3, 17, 17, 17, 0, 0},
2012 /* test if cpMin > cpMax */
2013 {15, 19, 18, 15, 18, 0, 1},
2014 {19, 15, 18, 15, 18, 0, 1}
2017 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
2022 cr.cpMin = setsel->min;
2023 cr.cpMax = setsel->max;
2024 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
2026 if (setsel->_exsetsel_todo_wine) {
2028 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2031 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2034 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
2036 if (setsel->_getsel_todo_wine) {
2038 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);
2041 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 static void test_EM_EXSETSEL(void)
2047 HWND hwndRichEdit = new_richedit(NULL);
2049 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
2051 /* sending some text to the window */
2052 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2053 /* 01234567890123456*/
2056 for (i = 0; i < num_tests; i++) {
2057 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
2060 DestroyWindow(hwndRichEdit);
2063 static void test_EM_REPLACESEL(int redraw)
2065 HWND hwndRichEdit = new_richedit(NULL);
2066 char buffer[1024] = {0};
2071 /* sending some text to the window */
2072 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2073 /* 01234567890123456*/
2076 /* FIXME add more tests */
2077 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
2078 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
2079 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
2080 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2081 r = strcmp(buffer, "testing");
2082 ok(0 == r, "expected %d, got %d\n", 0, r);
2084 DestroyWindow(hwndRichEdit);
2086 hwndRichEdit = new_richedit(NULL);
2088 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
2089 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
2091 /* Test behavior with carriage returns and newlines */
2092 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2093 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
2094 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
2095 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2096 r = strcmp(buffer, "RichEdit1");
2097 ok(0 == r, "expected %d, got %d\n", 0, r);
2099 getText.codepage = CP_ACP;
2100 getText.flags = GT_DEFAULT;
2101 getText.lpDefaultChar = NULL;
2102 getText.lpUsedDefChar = NULL;
2103 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2104 ok(strcmp(buffer, "RichEdit1") == 0,
2105 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
2107 /* Test number of lines reported after EM_REPLACESEL */
2108 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2109 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2111 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2112 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
2113 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
2114 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2115 r = strcmp(buffer, "RichEdit1\r\n");
2116 ok(0 == r, "expected %d, got %d\n", 0, r);
2118 getText.codepage = CP_ACP;
2119 getText.flags = GT_DEFAULT;
2120 getText.lpDefaultChar = NULL;
2121 getText.lpUsedDefChar = NULL;
2122 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2123 ok(strcmp(buffer, "RichEdit1\r") == 0,
2124 "EM_GETTEXTEX returned incorrect string\n");
2126 /* Test number of lines reported after EM_REPLACESEL */
2127 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2128 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2130 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
2131 EM_REPLACESEL. The general rule seems to be that Win98's riched20
2132 returns the number of characters *inserted* into the control (after
2133 required conversions), but WinXP's riched20 returns the number of
2134 characters interpreted from the original lParam. Wine's builtin riched20
2135 implements the WinXP behavior.
2137 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2138 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
2139 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
2140 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
2142 /* Test number of lines reported after EM_REPLACESEL */
2143 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2144 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2146 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2147 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2148 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2149 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2151 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2152 r = strcmp(buffer, "RichEdit1\r\n");
2153 ok(0 == r, "expected %d, got %d\n", 0, r);
2155 getText.codepage = CP_ACP;
2156 getText.flags = GT_DEFAULT;
2157 getText.lpDefaultChar = NULL;
2158 getText.lpUsedDefChar = NULL;
2159 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2160 ok(strcmp(buffer, "RichEdit1\r") == 0,
2161 "EM_GETTEXTEX returned incorrect string\n");
2163 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2164 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2165 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2166 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2168 /* The following tests show that richedit should handle the special \r\r\n
2169 sequence by turning it into a single space on insertion. However,
2170 EM_REPLACESEL on WinXP returns the number of characters in the original
2174 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2175 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
2176 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
2177 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2178 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2179 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2180 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2182 /* Test the actual string */
2184 getText.codepage = CP_ACP;
2185 getText.flags = GT_DEFAULT;
2186 getText.lpDefaultChar = NULL;
2187 getText.lpUsedDefChar = NULL;
2188 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2189 ok(strcmp(buffer, "\r\r") == 0,
2190 "EM_GETTEXTEX returned incorrect string\n");
2192 /* Test number of lines reported after EM_REPLACESEL */
2193 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2194 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2196 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2197 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
2198 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
2199 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
2200 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2201 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2202 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
2203 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
2205 /* Test the actual string */
2207 getText.codepage = CP_ACP;
2208 getText.flags = GT_DEFAULT;
2209 getText.lpDefaultChar = NULL;
2210 getText.lpUsedDefChar = NULL;
2211 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2212 ok(strcmp(buffer, " ") == 0,
2213 "EM_GETTEXTEX returned incorrect string\n");
2215 /* Test number of lines reported after EM_REPLACESEL */
2216 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2217 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2219 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2220 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
2221 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2222 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2223 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2224 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2225 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2226 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2228 /* Test the actual string */
2230 getText.codepage = CP_ACP;
2231 getText.flags = GT_DEFAULT;
2232 getText.lpDefaultChar = NULL;
2233 getText.lpUsedDefChar = NULL;
2234 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2235 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
2236 "EM_GETTEXTEX returned incorrect string\n");
2238 /* Test number of lines reported after EM_REPLACESEL */
2239 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2240 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2242 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2243 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
2244 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
2245 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
2246 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2247 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2248 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2249 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2251 /* Test the actual string */
2253 getText.codepage = CP_ACP;
2254 getText.flags = GT_DEFAULT;
2255 getText.lpDefaultChar = NULL;
2256 getText.lpUsedDefChar = NULL;
2257 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2258 ok(strcmp(buffer, " \r") == 0,
2259 "EM_GETTEXTEX returned incorrect string\n");
2261 /* Test number of lines reported after EM_REPLACESEL */
2262 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2263 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2265 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2266 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
2267 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
2268 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
2269 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2270 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2271 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
2272 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
2274 /* Test the actual string */
2276 getText.codepage = CP_ACP;
2277 getText.flags = GT_DEFAULT;
2278 getText.lpDefaultChar = NULL;
2279 getText.lpUsedDefChar = NULL;
2280 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2281 ok(strcmp(buffer, " \r\r") == 0,
2282 "EM_GETTEXTEX returned incorrect string\n");
2284 /* Test number of lines reported after EM_REPLACESEL */
2285 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2286 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2288 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2289 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
2290 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
2291 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
2292 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2293 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2294 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
2295 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
2297 /* Test the actual string */
2299 getText.codepage = CP_ACP;
2300 getText.flags = GT_DEFAULT;
2301 getText.lpDefaultChar = NULL;
2302 getText.lpUsedDefChar = NULL;
2303 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2304 ok(strcmp(buffer, "\rX\r\r\r") == 0,
2305 "EM_GETTEXTEX returned incorrect string\n");
2307 /* Test number of lines reported after EM_REPLACESEL */
2308 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2309 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
2311 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2312 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
2313 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
2314 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2315 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2316 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2317 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2319 /* Test the actual string */
2321 getText.codepage = CP_ACP;
2322 getText.flags = GT_DEFAULT;
2323 getText.lpDefaultChar = NULL;
2324 getText.lpUsedDefChar = NULL;
2325 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2326 ok(strcmp(buffer, "\r\r") == 0,
2327 "EM_GETTEXTEX returned incorrect string\n");
2329 /* Test number of lines reported after EM_REPLACESEL */
2330 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2331 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2333 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2334 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
2335 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2336 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2337 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2338 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2339 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2340 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2342 /* Test the actual string */
2344 getText.codepage = CP_ACP;
2345 getText.flags = GT_DEFAULT;
2346 getText.lpDefaultChar = NULL;
2347 getText.lpUsedDefChar = NULL;
2348 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2349 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
2350 "EM_GETTEXTEX returned incorrect string\n");
2352 /* Test number of lines reported after EM_REPLACESEL */
2353 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2354 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2356 DestroyWindow(hwndRichEdit);
2359 static void test_WM_PASTE(void)
2363 char buffer[1024] = {0};
2364 char key_info[][3] =
2366 /* VirtualKey, ScanCode, WM_CHAR code */
2367 {'C', 0x2e, 3}, /* Ctrl-C */
2368 {'X', 0x2d, 24}, /* Ctrl-X */
2369 {'V', 0x2f, 22}, /* Ctrl-V */
2370 {'Z', 0x2c, 26}, /* Ctrl-Z */
2371 {'Y', 0x15, 25}, /* Ctrl-Y */
2373 const char* text1 = "testing paste\r";
2374 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
2375 const char* text1_after = "testing paste\r\n";
2376 const char* text2 = "testing paste\r\rtesting paste";
2377 const char* text2_after = "testing paste\r\n\r\ntesting paste";
2378 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
2379 HWND hwndRichEdit = new_richedit(NULL);
2381 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
2382 messages, probably because it inspects the keyboard state itself.
2383 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
2385 #define SEND_CTRL_KEY(hwnd, k) \
2386 keybd_event(VK_CONTROL, 0x1d, 0, 0);\
2387 keybd_event(k[0], k[1], 0, 0);\
2388 keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
2389 keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
2390 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
2391 TranslateMessage(&msg); \
2392 DispatchMessage(&msg); \
2395 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
2396 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
2397 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
2398 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
2399 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
2401 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
2402 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
2404 SEND_CTRL_C(hwndRichEdit) /* Copy */
2405 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2406 SEND_CTRL_V(hwndRichEdit) /* Paste */
2407 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2408 /* Pasted text should be visible at this step */
2409 result = strcmp(text1_step1, buffer);
2411 "test paste: strcmp = %i\n", result);
2412 SEND_CTRL_Z(hwndRichEdit) /* Undo */
2413 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2414 /* Text should be the same as before (except for \r -> \r\n conversion) */
2415 result = strcmp(text1_after, buffer);
2417 "test paste: strcmp = %i\n", result);
2419 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
2420 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
2421 SEND_CTRL_C(hwndRichEdit) /* Copy */
2422 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2423 SEND_CTRL_V(hwndRichEdit) /* Paste */
2424 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2425 /* Pasted text should be visible at this step */
2426 result = strcmp(text3, buffer);
2428 "test paste: strcmp = %i\n", result);
2429 SEND_CTRL_Z(hwndRichEdit) /* Undo */
2430 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2431 /* Text should be the same as before (except for \r -> \r\n conversion) */
2432 result = strcmp(text2_after, buffer);
2434 "test paste: strcmp = %i\n", result);
2435 SEND_CTRL_Y(hwndRichEdit) /* Redo */
2436 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2437 /* Text should revert to post-paste state */
2438 result = strcmp(buffer,text3);
2440 "test paste: strcmp = %i\n", result);
2442 DestroyWindow(hwndRichEdit);
2445 static void test_EM_FORMATRANGE(void)
2450 HWND hwndRichEdit = new_richedit(NULL);
2452 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
2454 hdc = GetDC(hwndRichEdit);
2455 ok(hdc != NULL, "Could not get HDC\n");
2457 fr.hdc = fr.hdcTarget = hdc;
2458 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
2459 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
2460 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
2464 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2466 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2469 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2471 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
2477 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2479 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
2482 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2484 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2487 DestroyWindow(hwndRichEdit);
2490 static int nCallbackCount = 0;
2492 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
2495 const char text[] = {'t','e','s','t'};
2497 if (sizeof(text) <= cb)
2499 if ((int)dwCookie != nCallbackCount)
2505 memcpy (pbBuff, text, sizeof(text));
2506 *pcb = sizeof(text);
2513 return 1; /* indicates callback failed */
2516 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
2521 const char** str = (const char**)dwCookie;
2522 int size = strlen(*str);
2528 memcpy(pbBuff, *str, *pcb);
2535 static void test_EM_STREAMIN(void)
2537 HWND hwndRichEdit = new_richedit(NULL);
2540 char buffer[1024] = {0};
2542 const char * streamText0 = "{\\rtf1 TestSomeText}";
2543 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
2544 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
2546 const char * streamText1 =
2547 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
2548 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
2551 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
2552 const char * streamText2 =
2553 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
2554 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
2555 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
2556 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
2557 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
2558 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
2559 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
2561 const char * streamText3 = "RichEdit1";
2563 /* Minimal test without \par at the end */
2564 es.dwCookie = (DWORD_PTR)&streamText0;
2566 es.pfnCallback = test_EM_STREAMIN_esCallback;
2567 SendMessage(hwndRichEdit, EM_STREAMIN,
2568 (WPARAM)(SF_RTF), (LPARAM)&es);
2570 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2572 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
2573 result = strcmp (buffer,"TestSomeText");
2575 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
2576 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
2578 /* Native richedit 2.0 ignores last \par */
2579 es.dwCookie = (DWORD_PTR)&streamText0a;
2581 es.pfnCallback = test_EM_STREAMIN_esCallback;
2582 SendMessage(hwndRichEdit, EM_STREAMIN,
2583 (WPARAM)(SF_RTF), (LPARAM)&es);
2585 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2587 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
2588 result = strcmp (buffer,"TestSomeText");
2590 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
2591 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
2593 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
2594 es.dwCookie = (DWORD_PTR)&streamText0b;
2596 es.pfnCallback = test_EM_STREAMIN_esCallback;
2597 SendMessage(hwndRichEdit, EM_STREAMIN,
2598 (WPARAM)(SF_RTF), (LPARAM)&es);
2600 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2602 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
2603 result = strcmp (buffer,"TestSomeText\r\n");
2605 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
2606 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
2608 es.dwCookie = (DWORD_PTR)&streamText1;
2610 es.pfnCallback = test_EM_STREAMIN_esCallback;
2611 SendMessage(hwndRichEdit, EM_STREAMIN,
2612 (WPARAM)(SF_RTF), (LPARAM)&es);
2614 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2616 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
2617 result = strcmp (buffer,"TestSomeText");
2619 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2620 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
2622 es.dwCookie = (DWORD_PTR)&streamText2;
2624 SendMessage(hwndRichEdit, EM_STREAMIN,
2625 (WPARAM)(SF_RTF), (LPARAM)&es);
2627 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2629 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
2630 ok (strlen(buffer) == 0,
2631 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2632 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
2634 es.dwCookie = (DWORD_PTR)&streamText3;
2636 SendMessage(hwndRichEdit, EM_STREAMIN,
2637 (WPARAM)(SF_RTF), (LPARAM)&es);
2639 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2641 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
2642 ok (strlen(buffer) == 0,
2643 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
2644 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
2646 DestroyWindow(hwndRichEdit);
2649 static void test_EM_StreamIn_Undo(void)
2651 /* The purpose of this test is to determine when a EM_StreamIn should be
2652 * undoable. This is important because WM_PASTE currently uses StreamIn and
2653 * pasting should always be undoable but streaming isn't always.
2656 * StreamIn plain text without SFF_SELECTION.
2657 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
2658 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
2659 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
2660 * Feel free to add tests for other text modes or StreamIn things.
2664 HWND hwndRichEdit = new_richedit(NULL);
2667 char buffer[1024] = {0};
2668 const char randomtext[] = "Some text";
2670 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
2672 /* StreamIn, no SFF_SELECTION */
2673 es.dwCookie = nCallbackCount;
2674 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2675 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2676 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2677 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
2678 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2679 result = strcmp (buffer,"test");
2681 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2683 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2684 ok (result == FALSE,
2685 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
2687 /* StreamIn, SFF_SELECTION, but nothing selected */
2688 es.dwCookie = nCallbackCount;
2689 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2690 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2691 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2692 SendMessage(hwndRichEdit, EM_STREAMIN,
2693 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2694 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2695 result = strcmp (buffer,"testSome text");
2697 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2699 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2701 "EM_STREAMIN with SFF_SELECTION but no selection set "
2702 "should create an undo\n");
2704 /* StreamIn, SFF_SELECTION, with a selection */
2705 es.dwCookie = nCallbackCount;
2706 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2707 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2708 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
2709 SendMessage(hwndRichEdit, EM_STREAMIN,
2710 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2711 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2712 result = strcmp (buffer,"Sometesttext");
2714 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2716 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2718 "EM_STREAMIN with SFF_SELECTION and selection set "
2719 "should create an undo\n");
2723 static BOOL is_em_settextex_supported(HWND hwnd)
2725 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
2726 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
2729 static void test_unicode_conversions(void)
2731 static const WCHAR tW[] = {'t',0};
2732 static const WCHAR teW[] = {'t','e',0};
2733 static const WCHAR textW[] = {'t','e','s','t',0};
2734 static const char textA[] = "test";
2738 int is_win9x, em_settextex_supported, ret;
2740 is_win9x = GetVersion() & 0x80000000;
2742 #define set_textA(hwnd, wm_set_text, txt) \
2744 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
2745 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2746 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2747 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2748 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
2750 #define expect_textA(hwnd, wm_get_text, txt) \
2752 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2753 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2754 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2755 memset(bufA, 0xAA, sizeof(bufA)); \
2756 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2757 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2758 ret = lstrcmpA(bufA, txt); \
2759 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
2762 #define set_textW(hwnd, wm_set_text, txt) \
2764 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
2765 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2766 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2767 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2768 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
2770 #define expect_textW(hwnd, wm_get_text, txt) \
2772 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
2773 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2774 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2775 memset(bufW, 0xAA, sizeof(bufW)); \
2778 assert(wm_get_text == EM_GETTEXTEX); \
2779 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2780 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2784 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2785 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
2787 ret = lstrcmpW(bufW, txt); \
2788 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
2790 #define expect_empty(hwnd, wm_get_text) \
2792 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2793 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2794 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2795 memset(bufA, 0xAA, sizeof(bufA)); \
2796 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2797 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
2798 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
2801 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2802 0, 0, 200, 60, 0, 0, 0, 0);
2803 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2805 ret = IsWindowUnicode(hwnd);
2807 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
2809 ok(ret, "RichEdit20W should be unicode under NT\n");
2811 /* EM_SETTEXTEX is supported starting from version 3.0 */
2812 em_settextex_supported = is_em_settextex_supported(hwnd);
2813 trace("EM_SETTEXTEX is %ssupported on this platform\n",
2814 em_settextex_supported ? "" : "NOT ");
2816 expect_empty(hwnd, WM_GETTEXT);
2817 expect_empty(hwnd, EM_GETTEXTEX);
2819 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
2820 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2821 expect_textA(hwnd, WM_GETTEXT, "t");
2822 expect_textA(hwnd, EM_GETTEXTEX, "t");
2823 expect_textW(hwnd, EM_GETTEXTEX, tW);
2825 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
2826 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2827 expect_textA(hwnd, WM_GETTEXT, "te");
2828 expect_textA(hwnd, EM_GETTEXTEX, "te");
2829 expect_textW(hwnd, EM_GETTEXTEX, teW);
2831 set_textA(hwnd, WM_SETTEXT, NULL);
2832 expect_empty(hwnd, WM_GETTEXT);
2833 expect_empty(hwnd, EM_GETTEXTEX);
2836 set_textA(hwnd, WM_SETTEXT, textW);
2838 set_textA(hwnd, WM_SETTEXT, textA);
2839 expect_textA(hwnd, WM_GETTEXT, textA);
2840 expect_textA(hwnd, EM_GETTEXTEX, textA);
2841 expect_textW(hwnd, EM_GETTEXTEX, textW);
2843 if (em_settextex_supported)
2845 set_textA(hwnd, EM_SETTEXTEX, textA);
2846 expect_textA(hwnd, WM_GETTEXT, textA);
2847 expect_textA(hwnd, EM_GETTEXTEX, textA);
2848 expect_textW(hwnd, EM_GETTEXTEX, textW);
2853 set_textW(hwnd, WM_SETTEXT, textW);
2854 expect_textW(hwnd, WM_GETTEXT, textW);
2855 expect_textA(hwnd, WM_GETTEXT, textA);
2856 expect_textW(hwnd, EM_GETTEXTEX, textW);
2857 expect_textA(hwnd, EM_GETTEXTEX, textA);
2859 if (em_settextex_supported)
2861 set_textW(hwnd, EM_SETTEXTEX, textW);
2862 expect_textW(hwnd, WM_GETTEXT, textW);
2863 expect_textA(hwnd, WM_GETTEXT, textA);
2864 expect_textW(hwnd, EM_GETTEXTEX, textW);
2865 expect_textA(hwnd, EM_GETTEXTEX, textA);
2868 DestroyWindow(hwnd);
2870 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
2871 0, 0, 200, 60, 0, 0, 0, 0);
2872 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2874 ret = IsWindowUnicode(hwnd);
2875 ok(!ret, "RichEdit20A should NOT be unicode\n");
2877 set_textA(hwnd, WM_SETTEXT, textA);
2878 expect_textA(hwnd, WM_GETTEXT, textA);
2879 expect_textA(hwnd, EM_GETTEXTEX, textA);
2880 expect_textW(hwnd, EM_GETTEXTEX, textW);
2882 if (em_settextex_supported)
2884 set_textA(hwnd, EM_SETTEXTEX, textA);
2885 expect_textA(hwnd, WM_GETTEXT, textA);
2886 expect_textA(hwnd, EM_GETTEXTEX, textA);
2887 expect_textW(hwnd, EM_GETTEXTEX, textW);
2892 set_textW(hwnd, WM_SETTEXT, textW);
2893 expect_textW(hwnd, WM_GETTEXT, textW);
2894 expect_textA(hwnd, WM_GETTEXT, textA);
2895 expect_textW(hwnd, EM_GETTEXTEX, textW);
2896 expect_textA(hwnd, EM_GETTEXTEX, textA);
2898 if (em_settextex_supported)
2900 set_textW(hwnd, EM_SETTEXTEX, textW);
2901 expect_textW(hwnd, WM_GETTEXT, textW);
2902 expect_textA(hwnd, WM_GETTEXT, textA);
2903 expect_textW(hwnd, EM_GETTEXTEX, textW);
2904 expect_textA(hwnd, EM_GETTEXTEX, textA);
2907 DestroyWindow(hwnd);
2910 static void test_WM_CHAR(void)
2914 const char * char_list = "abc\rabc\r";
2915 const char * expected_content_single = "abcabc";
2916 const char * expected_content_multi = "abc\r\nabc\r\n";
2917 char buffer[64] = {0};
2920 /* single-line control must IGNORE carriage returns */
2921 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2922 0, 0, 200, 60, 0, 0, 0, 0);
2923 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2926 while (*p != '\0') {
2927 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2928 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2929 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2930 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2934 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2935 ret = strcmp(buffer, expected_content_single);
2936 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2938 DestroyWindow(hwnd);
2940 /* multi-line control inserts CR normally */
2941 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
2942 0, 0, 200, 60, 0, 0, 0, 0);
2943 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2946 while (*p != '\0') {
2947 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2948 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2949 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2950 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2954 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2955 ret = strcmp(buffer, expected_content_multi);
2956 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2958 DestroyWindow(hwnd);
2961 static void test_EM_GETTEXTLENGTHEX(void)
2964 GETTEXTLENGTHEX gtl;
2966 const char * base_string = "base string";
2967 const char * test_string = "a\nb\n\n\r\n";
2968 const char * test_string_after = "a";
2969 const char * test_string_2 = "a\rtest\rstring";
2970 char buffer[64] = {0};
2973 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2974 0, 0, 200, 60, 0, 0, 0, 0);
2975 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2977 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2978 gtl.codepage = CP_ACP;
2979 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2980 ok(ret == 0, "ret %d\n",ret);
2982 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2983 gtl.codepage = CP_ACP;
2984 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2985 ok(ret == 0, "ret %d\n",ret);
2987 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
2989 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2990 gtl.codepage = CP_ACP;
2991 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2992 ok(ret == strlen(base_string), "ret %d\n",ret);
2994 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2995 gtl.codepage = CP_ACP;
2996 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2997 ok(ret == strlen(base_string), "ret %d\n",ret);
2999 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3001 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3002 gtl.codepage = CP_ACP;
3003 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3004 ok(ret == 1, "ret %d\n",ret);
3006 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3007 gtl.codepage = CP_ACP;
3008 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3009 ok(ret == 1, "ret %d\n",ret);
3011 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3012 ret = strcmp(buffer, test_string_after);
3013 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3015 DestroyWindow(hwnd);
3018 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
3019 0, 0, 200, 60, 0, 0, 0, 0);
3020 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3022 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3023 gtl.codepage = CP_ACP;
3024 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3025 ok(ret == 0, "ret %d\n",ret);
3027 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3028 gtl.codepage = CP_ACP;
3029 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3030 ok(ret == 0, "ret %d\n",ret);
3032 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
3034 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3035 gtl.codepage = CP_ACP;
3036 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3037 ok(ret == strlen(base_string), "ret %d\n",ret);
3039 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3040 gtl.codepage = CP_ACP;
3041 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3042 ok(ret == strlen(base_string), "ret %d\n",ret);
3044 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
3046 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3047 gtl.codepage = CP_ACP;
3048 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3049 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
3051 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3052 gtl.codepage = CP_ACP;
3053 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3054 ok(ret == strlen(test_string_2), "ret %d\n",ret);
3056 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3058 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3059 gtl.codepage = CP_ACP;
3060 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3061 ok(ret == 10, "ret %d\n",ret);
3063 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3064 gtl.codepage = CP_ACP;
3065 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3066 ok(ret == 6, "ret %d\n",ret);
3068 DestroyWindow(hwnd);
3072 /* globals that parent and child access when checking event masks & notifications */
3073 static HWND eventMaskEditHwnd = 0;
3074 static int queriedEventMask;
3075 static int watchForEventMask = 0;
3077 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
3078 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3080 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
3082 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3084 return DefWindowProcA(hwnd, message, wParam, lParam);
3087 /* test event masks in combination with WM_COMMAND */
3088 static void test_eventMask(void)
3093 const char text[] = "foo bar\n";
3096 /* register class to capture WM_COMMAND */
3098 cls.lpfnWndProc = ParentMsgCheckProcA;
3101 cls.hInstance = GetModuleHandleA(0);
3103 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3104 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3105 cls.lpszMenuName = NULL;
3106 cls.lpszClassName = "EventMaskParentClass";
3107 if(!RegisterClassA(&cls)) assert(0);
3109 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3110 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3111 ok (parent != 0, "Failed to create parent window\n");
3113 eventMaskEditHwnd = new_richedit(parent);
3114 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
3116 eventMask = ENM_CHANGE | ENM_UPDATE;
3117 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
3118 ok(ret == ENM_NONE, "wrong event mask\n");
3119 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3120 ok(ret == eventMask, "failed to set event mask\n");
3122 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
3123 queriedEventMask = 0; /* initialize to something other than we expect */
3124 watchForEventMask = EN_CHANGE;
3125 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
3126 ok(ret == TRUE, "failed to set text\n");
3127 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
3128 notification in response to WM_SETTEXT */
3129 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
3130 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
3134 static int received_WM_NOTIFY = 0;
3135 static int modify_at_WM_NOTIFY = 0;
3136 static HWND hwndRichedit_WM_NOTIFY;
3138 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3140 if(message == WM_NOTIFY)
3142 received_WM_NOTIFY = 1;
3143 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
3145 return DefWindowProcA(hwnd, message, wParam, lParam);
3148 static void test_WM_NOTIFY(void)
3154 /* register class to capture WM_NOTIFY */
3156 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
3159 cls.hInstance = GetModuleHandleA(0);
3161 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3162 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3163 cls.lpszMenuName = NULL;
3164 cls.lpszClassName = "WM_NOTIFY_ParentClass";
3165 if(!RegisterClassA(&cls)) assert(0);
3167 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3168 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3169 ok (parent != 0, "Failed to create parent window\n");
3171 hwndRichedit_WM_NOTIFY = new_richedit(parent);
3172 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
3174 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
3176 /* Notifications for selection change should only be sent when selection
3177 actually changes. EM_SETCHARFORMAT is one message that calls
3178 ME_CommitUndo, which should check whether message should be sent */
3179 received_WM_NOTIFY = 0;
3180 cf2.cbSize = sizeof(CHARFORMAT2);
3181 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3183 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3184 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3185 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
3186 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3188 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
3190 received_WM_NOTIFY = 0;
3191 modify_at_WM_NOTIFY = 0;
3192 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3193 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3194 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3196 received_WM_NOTIFY = 0;
3197 modify_at_WM_NOTIFY = 0;
3198 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
3199 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3201 received_WM_NOTIFY = 0;
3202 modify_at_WM_NOTIFY = 0;
3203 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3204 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3205 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3207 DestroyWindow(hwndRichedit_WM_NOTIFY);
3208 DestroyWindow(parent);
3211 START_TEST( editor )
3216 /* Must explicitly LoadLibrary(). The test has no references to functions in
3217 * RICHED20.DLL, so the linker doesn't actually link to it. */
3218 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
3219 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
3223 test_EM_SCROLLCARET();
3226 test_EM_LINELENGTH();
3227 test_EM_SETCHARFORMAT();
3228 test_EM_SETTEXTMODE();
3229 test_TM_PLAINTEXT();
3230 test_EM_SETOPTIONS();
3232 test_EM_GETTEXTRANGE();
3233 test_EM_GETSELTEXT();
3234 test_EM_AUTOURLDETECT();
3235 test_EM_SETUNDOLIMIT();
3237 test_EM_SETTEXTEX();
3238 test_EM_LIMITTEXT();
3239 test_EM_EXLIMITTEXT();
3240 test_EM_GETLIMITTEXT();
3242 test_EM_GETMODIFY();
3246 test_EM_STREAMOUT();
3247 test_EM_StreamIn_Undo();
3248 test_EM_FORMATRANGE();
3249 test_unicode_conversions();
3250 test_EM_GETTEXTLENGTHEX();
3251 test_EM_REPLACESEL(1);
3252 test_EM_REPLACESEL(0);
3256 /* Set the environment variable WINETEST_RICHED20 to keep windows
3257 * responsive and open for 30 seconds. This is useful for debugging.
3259 * The message pump uses PeekMessage() to empty the queue and then sleeps for
3260 * 50ms before retrying the queue. */
3261 end = time(NULL) + 30;
3262 if (getenv( "WINETEST_RICHED20" )) {
3263 while (time(NULL) < end) {
3264 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
3265 TranslateMessage(&msg);
3266 DispatchMessage(&msg);
3273 OleFlushClipboard();
3274 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());