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, expected %d\n",
145 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
148 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
152 int expected_end_loc;
154 memset(&ft, 0, sizeof(ft));
155 ft.chrg.cpMin = f->start;
156 ft.chrg.cpMax = f->end;
157 ft.lpstrText = f->needle;
158 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
159 ok(findloc == f->expected_loc,
160 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
161 name, id, f->needle, f->start, f->end, f->flags, findloc);
162 ok(ft.chrgText.cpMin == f->expected_loc,
163 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
164 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
165 expected_end_loc = ((f->expected_loc == -1) ? -1
166 : f->expected_loc + strlen(f->needle));
167 ok(ft.chrgText.cpMax == expected_end_loc,
168 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
169 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
172 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
177 for (i = 0; i < num_tests; i++) {
178 if (find[i]._todo_wine) {
180 check_EM_FINDTEXT(hwnd, name, &find[i], i);
181 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
184 check_EM_FINDTEXT(hwnd, name, &find[i], i);
185 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
190 static void test_EM_FINDTEXT(void)
192 HWND hwndRichEdit = new_richedit(NULL);
195 /* Empty rich edit control */
196 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
197 sizeof(find_tests)/sizeof(struct find_s));
199 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
202 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
203 sizeof(find_tests2)/sizeof(struct find_s));
205 /* Setting a format on an arbitrary range should have no effect in search
206 results. This tests correct offset reporting across runs. */
207 cf2.cbSize = sizeof(CHARFORMAT2);
208 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
210 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
211 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
212 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
213 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
215 /* Haystack text, again */
216 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
217 sizeof(find_tests2)/sizeof(struct find_s));
219 /* Yet another range */
220 cf2.dwMask = CFM_BOLD | cf2.dwMask;
221 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
222 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
223 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
225 /* Haystack text, again */
226 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
227 sizeof(find_tests2)/sizeof(struct find_s));
229 DestroyWindow(hwndRichEdit);
232 static const struct getline_s {
237 {0, 10, "foo bar\r"},
242 /* Buffer smaller than line length */
248 static void test_EM_GETLINE(void)
251 HWND hwndRichEdit = new_richedit(NULL);
252 static const int nBuf = 1024;
253 char dest[1024], origdest[1024];
254 const char text[] = "foo bar\n"
258 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
260 memset(origdest, 0xBB, nBuf);
261 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
264 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
265 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
266 memset(dest, 0xBB, nBuf);
267 *(WORD *) dest = gl[i].buffer_len;
269 /* EM_GETLINE appends a "\r\0" to the end of the line
270 * nCopied counts up to and including the '\r' */
271 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
272 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
274 /* two special cases since a parameter is passed via dest */
275 if (gl[i].buffer_len == 0)
276 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
278 else if (gl[i].buffer_len == 1)
279 ok(dest[0] == gl[i].text[0] && !dest[1] &&
280 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
283 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
284 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
285 ok(!strncmp(dest + expected_bytes_written, origdest
286 + expected_bytes_written, nBuf - expected_bytes_written),
287 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
291 DestroyWindow(hwndRichEdit);
294 static void test_EM_LINELENGTH(void)
296 HWND hwndRichEdit = new_richedit(NULL);
302 int offset_test[10][2] = {
317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
319 for (i = 0; i < 10; i++) {
320 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
321 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
322 offset_test[i][0], result, offset_test[i][1]);
325 DestroyWindow(hwndRichEdit);
328 static int get_scroll_pos_y(HWND hwnd)
331 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
332 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
336 static void move_cursor(HWND hwnd, long charindex)
339 cr.cpMax = charindex;
340 cr.cpMin = charindex;
341 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
344 static void line_scroll(HWND hwnd, int amount)
346 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
349 static void test_EM_SCROLLCARET(void)
352 HWND hwndRichEdit = new_richedit(NULL);
353 const char text[] = "aa\n"
354 "this is a long line of text that should be longer than the "
363 /* Can't verify this */
364 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
366 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
368 /* Caret above visible window */
369 line_scroll(hwndRichEdit, 3);
370 prevY = get_scroll_pos_y(hwndRichEdit);
371 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
372 curY = get_scroll_pos_y(hwndRichEdit);
373 ok(prevY != curY, "%d == %d\n", prevY, curY);
375 /* Caret below visible window */
376 move_cursor(hwndRichEdit, sizeof(text) - 1);
377 line_scroll(hwndRichEdit, -3);
378 prevY = get_scroll_pos_y(hwndRichEdit);
379 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
380 curY = get_scroll_pos_y(hwndRichEdit);
381 ok(prevY != curY, "%d == %d\n", prevY, curY);
383 /* Caret in visible window */
384 move_cursor(hwndRichEdit, sizeof(text) - 2);
385 prevY = get_scroll_pos_y(hwndRichEdit);
386 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
387 curY = get_scroll_pos_y(hwndRichEdit);
388 ok(prevY == curY, "%d != %d\n", prevY, curY);
390 /* Caret still in visible window */
391 line_scroll(hwndRichEdit, -1);
392 prevY = get_scroll_pos_y(hwndRichEdit);
393 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
394 curY = get_scroll_pos_y(hwndRichEdit);
395 ok(prevY == curY, "%d != %d\n", prevY, curY);
397 DestroyWindow(hwndRichEdit);
400 static void test_EM_SETCHARFORMAT(void)
402 HWND hwndRichEdit = new_richedit(NULL);
406 /* Invalid flags, CHARFORMAT2 structure blanked out */
407 memset(&cf2, 0, sizeof(cf2));
408 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
410 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
412 /* A valid flag, CHARFORMAT2 structure blanked out */
413 memset(&cf2, 0, sizeof(cf2));
414 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
416 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
418 /* A valid flag, CHARFORMAT2 structure blanked out */
419 memset(&cf2, 0, sizeof(cf2));
420 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
422 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
424 /* A valid flag, CHARFORMAT2 structure blanked out */
425 memset(&cf2, 0, sizeof(cf2));
426 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
428 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
430 /* A valid flag, CHARFORMAT2 structure blanked out */
431 memset(&cf2, 0, sizeof(cf2));
432 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
434 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
436 /* Invalid flags, CHARFORMAT2 structure minimally filled */
437 memset(&cf2, 0, sizeof(cf2));
438 cf2.cbSize = sizeof(CHARFORMAT2);
439 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
441 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
443 /* A valid flag, CHARFORMAT2 structure minimally filled */
444 memset(&cf2, 0, sizeof(cf2));
445 cf2.cbSize = sizeof(CHARFORMAT2);
446 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
448 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
450 /* A valid flag, CHARFORMAT2 structure minimally filled */
451 memset(&cf2, 0, sizeof(cf2));
452 cf2.cbSize = sizeof(CHARFORMAT2);
453 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
455 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
457 /* A valid flag, CHARFORMAT2 structure minimally filled */
458 memset(&cf2, 0, sizeof(cf2));
459 cf2.cbSize = sizeof(CHARFORMAT2);
460 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
462 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
464 /* A valid flag, CHARFORMAT2 structure minimally filled */
465 memset(&cf2, 0, sizeof(cf2));
466 cf2.cbSize = sizeof(CHARFORMAT2);
467 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
469 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
471 cf2.cbSize = sizeof(CHARFORMAT2);
472 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
475 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
476 cf2.cbSize = sizeof(CHARFORMAT2);
477 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
479 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
480 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
482 /* wParam==0 is default char format, does not set modify */
483 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
484 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
485 ok(rc == 0, "Text marked as modified, expected not modified!\n");
486 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
487 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
488 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
489 ok(rc == 0, "Text marked as modified, expected not modified!\n");
491 /* wParam==SCF_SELECTION sets modify if nonempty selection */
492 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
493 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
494 ok(rc == 0, "Text marked as modified, expected not modified!\n");
495 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
496 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
497 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
498 ok(rc == 0, "Text marked as modified, expected not modified!\n");
500 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
501 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
502 ok(rc == 0, "Text marked as modified, expected not modified!\n");
503 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
504 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
505 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
506 ok(rc == 0, "Text marked as modified, expected not modified!\n");
507 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
508 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
509 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
510 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
511 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
513 /* wParam==SCF_ALL sets modify regardless of whether text is present */
514 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
515 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
516 ok(rc == 0, "Text marked as modified, expected not modified!\n");
517 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
518 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
519 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
520 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
522 DestroyWindow(hwndRichEdit);
525 static void test_EM_SETTEXTMODE(void)
527 HWND hwndRichEdit = new_richedit(NULL);
528 CHARFORMAT2 cf2, cf2test;
532 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
533 /*Insert text into the control*/
535 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
537 /*Attempt to change the control to plain text mode*/
538 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
539 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
541 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
542 If rich text is pasted, it should have the same formatting as the rest
543 of the text in the control*/
546 *NOTE: If the default text was already italicized, the test will simply
547 reverse; in other words, it will copy a regular "wine" into a plain
548 text window that uses an italicized format*/
549 cf2.cbSize = sizeof(CHARFORMAT2);
550 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
553 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
554 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
556 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
557 ok(rc == 0, "Text marked as modified, expected not modified!\n");
559 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
560 however, SCF_ALL has been implemented*/
561 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
562 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
564 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
565 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
567 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
569 /*Select the string "wine"*/
572 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
574 /*Copy the italicized "wine" to the clipboard*/
575 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
577 /*Reset the formatting to default*/
578 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
579 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
580 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
582 /*Clear the text in the control*/
583 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
585 /*Switch to Plain Text Mode*/
586 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
587 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
589 /*Input "wine" again in normal format*/
590 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
592 /*Paste the italicized "wine" into the control*/
593 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
595 /*Select a character from the first "wine" string*/
598 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
600 /*Retrieve its formatting*/
601 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
604 /*Select a character from the second "wine" string*/
607 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
609 /*Retrieve its formatting*/
610 cf2test.cbSize = sizeof(CHARFORMAT2);
611 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
614 /*Compare the two formattings*/
615 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
616 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
617 cf2.dwEffects, cf2test.dwEffects);
618 /*Test TM_RICHTEXT by: switching back to Rich Text mode
619 printing "wine" in the current format(normal)
620 pasting "wine" from the clipboard(italicized)
621 comparing the two formats(should differ)*/
623 /*Attempt to switch with text in control*/
624 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
625 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
628 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
630 /*Switch into Rich Text mode*/
631 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
632 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
634 /*Print "wine" in normal formatting into the control*/
635 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
637 /*Paste italicized "wine" into the control*/
638 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
640 /*Select text from the first "wine" string*/
643 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
645 /*Retrieve its formatting*/
646 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
649 /*Select text from the second "wine" string*/
652 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
654 /*Retrieve its formatting*/
655 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
658 /*Test that the two formattings are not the same*/
659 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
660 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
661 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
663 DestroyWindow(hwndRichEdit);
666 static void test_TM_PLAINTEXT(void)
668 /*Tests plain text properties*/
670 HWND hwndRichEdit = new_richedit(NULL);
671 CHARFORMAT2 cf2, cf2test;
675 /*Switch to plain text mode*/
677 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
678 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
680 /*Fill control with text*/
682 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
684 /*Select some text and bold it*/
688 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
689 cf2.cbSize = sizeof(CHARFORMAT2);
690 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
693 cf2.dwMask = CFM_BOLD | cf2.dwMask;
694 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
696 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
697 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
699 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
700 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
702 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
703 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
705 /*Get the formatting of those characters*/
707 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
709 /*Get the formatting of some other characters*/
710 cf2test.cbSize = sizeof(CHARFORMAT2);
713 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
714 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
716 /*Test that they are the same as plain text allows only one formatting*/
718 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
719 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
720 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
722 /*Fill the control with a "wine" string, which when inserted will be bold*/
724 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
726 /*Copy the bolded "wine" string*/
730 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
731 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
733 /*Swap back to rich text*/
735 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
736 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
738 /*Set the default formatting to bold italics*/
740 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
741 cf2.dwMask |= CFM_ITALIC;
742 cf2.dwEffects ^= CFE_ITALIC;
743 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
744 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
746 /*Set the text in the control to "wine", which will be bold and italicized*/
748 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
750 /*Paste the plain text "wine" string, which should take the insert
751 formatting, which at the moment is bold italics*/
753 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
755 /*Select the first "wine" string and retrieve its formatting*/
759 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
760 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
762 /*Select the second "wine" string and retrieve its formatting*/
766 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
767 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
769 /*Compare the two formattings. They should be the same.*/
771 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
772 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
773 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
774 DestroyWindow(hwndRichEdit);
777 static void test_WM_GETTEXT(void)
779 HWND hwndRichEdit = new_richedit(NULL);
780 static const char text[] = "Hello. My name is RichEdit!";
781 static const char text2[] = "Hello. My name is RichEdit!\r";
782 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
783 char buffer[1024] = {0};
786 /* Baseline test with normal-sized buffer */
787 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
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 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
792 result = strcmp(buffer,text);
794 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
796 /* Test for returned value of WM_GETTEXTLENGTH */
797 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
798 ok(result == lstrlen(text),
799 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
800 result, lstrlen(text));
802 /* Test for behavior in overflow case */
803 memset(buffer, 0, 1024);
804 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
806 result == lstrlenA(text) - 1, /* XP, win2k3 */
807 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
808 result = strcmp(buffer,text);
810 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
812 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
814 /* Baseline test with normal-sized buffer and carriage return */
815 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
816 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
817 ok(result == lstrlen(buffer),
818 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
819 result = strcmp(buffer,text2_after);
821 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
823 /* Test for returned value of WM_GETTEXTLENGTH */
824 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
825 ok(result == lstrlen(text2_after),
826 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
827 result, lstrlen(text2_after));
829 /* Test for behavior of CRLF conversion in case of overflow */
830 memset(buffer, 0, 1024);
831 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
833 result == lstrlenA(text2) - 1, /* XP, win2k3 */
834 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
835 result = strcmp(buffer,text2);
837 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
839 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
841 DestroyWindow(hwndRichEdit);
844 static void test_EM_GETTEXTRANGE(void)
846 HWND hwndRichEdit = new_richedit(NULL);
847 const char * text1 = "foo bar\r\nfoo bar";
848 const char * text2 = "foo bar\rfoo bar";
849 const char * expect = "bar\rfoo";
850 char buffer[1024] = {0};
852 TEXTRANGEA textRange;
854 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
856 textRange.lpstrText = buffer;
857 textRange.chrg.cpMin = 4;
858 textRange.chrg.cpMax = 11;
859 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
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 textRange.lpstrText = buffer;
866 textRange.chrg.cpMin = 4;
867 textRange.chrg.cpMax = 11;
868 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
869 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
870 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
872 DestroyWindow(hwndRichEdit);
875 static void test_EM_GETSELTEXT(void)
877 HWND hwndRichEdit = new_richedit(NULL);
878 const char * text1 = "foo bar\r\nfoo bar";
879 const char * text2 = "foo bar\rfoo bar";
880 const char * expect = "bar\rfoo";
881 char buffer[1024] = {0};
884 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
886 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
887 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
888 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
889 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
891 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
893 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
894 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
895 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
896 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
898 DestroyWindow(hwndRichEdit);
901 /* FIXME: need to test unimplemented options and robustly test wparam */
902 static void test_EM_SETOPTIONS(void)
904 HWND hwndRichEdit = new_richedit(NULL);
905 static const char text[] = "Hello. My name is RichEdit!";
906 char buffer[1024] = {0};
908 /* NEGATIVE TESTING - NO OPTIONS SET */
909 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
910 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
912 /* testing no readonly by sending 'a' to the control*/
913 SetFocus(hwndRichEdit);
914 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
915 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
917 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
918 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
920 /* READONLY - sending 'a' to the control */
921 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
922 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
923 SetFocus(hwndRichEdit);
924 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
925 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
926 ok(buffer[0]==text[0],
927 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
929 DestroyWindow(hwndRichEdit);
932 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
934 CHARFORMAT2W text_format;
935 text_format.cbSize = sizeof(text_format);
936 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
937 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
938 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
941 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
943 int link_present = 0;
945 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
947 { /* control text is url; should get CFE_LINK */
948 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
952 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
956 static HWND new_static_wnd(HWND parent) {
957 return new_window("Static", 0, parent);
960 static void test_EM_AUTOURLDETECT(void)
967 {"http://www.winehq.org", 1},
968 {"http//winehq.org", 0},
969 {"ww.winehq.org", 0},
970 {"www.winehq.org", 1},
971 {"ftp://192.168.1.1", 1},
972 {"ftp//192.168.1.1", 0},
973 {"mailto:your@email.com", 1},
974 {"prospero:prosperoserver", 1},
976 {"news:newserver", 1},
977 {"wais:waisserver", 1}
982 HWND hwndRichEdit, parent;
984 /* All of the following should cause the URL to be detected */
985 const char * templates_delim[] = {
986 "This is some text with X on it",
987 "This is some text with (X) on it",
988 "This is some text with X\r on it",
989 "This is some text with ---X--- on it",
990 "This is some text with \"X\" on it",
991 "This is some text with 'X' on it",
992 "This is some text with 'X' on it",
993 "This is some text with :X: on it",
995 "This text ends with X",
997 "This is some text with X) on it",
998 "This is some text with X--- on it",
999 "This is some text with X\" on it",
1000 "This is some text with X' on it",
1001 "This is some text with X: on it",
1003 "This is some text with (X on it",
1004 "This is some text with \rX on it",
1005 "This is some text with ---X on it",
1006 "This is some text with \"X on it",
1007 "This is some text with 'X on it",
1008 "This is some text with :X on it",
1010 /* None of these should cause the URL to be detected */
1011 const char * templates_non_delim[] = {
1012 "This is some text with |X| on it",
1013 "This is some text with *X* on it",
1014 "This is some text with /X/ on it",
1015 "This is some text with +X+ on it",
1016 "This is some text with %X% on it",
1017 "This is some text with #X# on it",
1018 "This is some text with @X@ on it",
1019 "This is some text with \\X\\ on it",
1020 "This is some text with |X on it",
1021 "This is some text with *X on it",
1022 "This is some text with /X on it",
1023 "This is some text with +X on it",
1024 "This is some text with %X on it",
1025 "This is some text with #X on it",
1026 "This is some text with @X on it",
1027 "This is some text with \\X on it",
1029 /* All of these cause the URL detection to be extended by one more byte,
1030 thus demonstrating that the tested character is considered as part
1032 const char * templates_xten_delim[] = {
1033 "This is some text with X| on it",
1034 "This is some text with X* on it",
1035 "This is some text with X/ on it",
1036 "This is some text with X+ on it",
1037 "This is some text with X% on it",
1038 "This is some text with X# on it",
1039 "This is some text with X@ on it",
1040 "This is some text with X\\ on it",
1044 parent = new_static_wnd(NULL);
1045 hwndRichEdit = new_richedit(parent);
1046 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1047 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1048 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1049 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1050 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1051 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1052 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1053 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1054 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1055 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1056 /* for each url, check the text to see if CFE_LINK effect is present */
1057 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1059 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1060 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1061 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1063 /* Link detection should happen immediately upon WM_SETTEXT */
1064 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1065 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1066 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1068 DestroyWindow(hwndRichEdit);
1070 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1071 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1072 hwndRichEdit = new_richedit(parent);
1074 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1079 at_pos = strchr(templates_delim[j], 'X');
1080 at_offset = at_pos - templates_delim[j];
1081 strncpy(buffer, templates_delim[j], at_offset);
1082 buffer[at_offset] = '\0';
1083 strcat(buffer, urls[i].text);
1084 strcat(buffer, templates_delim[j] + at_offset + 1);
1085 end_offset = at_offset + strlen(urls[i].text);
1087 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1088 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1090 /* This assumes no templates start with the URL itself, and that they
1091 have at least two characters before the URL text */
1092 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1093 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1094 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1095 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1096 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1097 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1101 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1102 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1103 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1104 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1108 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1109 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1110 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1111 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1113 if (buffer[end_offset] != '\0')
1115 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1116 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1117 if (buffer[end_offset +1] != '\0')
1119 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1120 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1125 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1130 at_pos = strchr(templates_non_delim[j], 'X');
1131 at_offset = at_pos - templates_non_delim[j];
1132 strncpy(buffer, templates_non_delim[j], at_offset);
1133 buffer[at_offset] = '\0';
1134 strcat(buffer, urls[i].text);
1135 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1136 end_offset = at_offset + strlen(urls[i].text);
1138 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1139 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1141 /* This assumes no templates start with the URL itself, and that they
1142 have at least two characters before the URL text */
1143 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1144 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1145 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1146 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1147 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1148 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1150 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1151 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1152 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1153 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1154 if (buffer[end_offset] != '\0')
1156 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1157 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1158 if (buffer[end_offset +1] != '\0')
1160 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1161 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1166 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1171 at_pos = strchr(templates_xten_delim[j], 'X');
1172 at_offset = at_pos - templates_xten_delim[j];
1173 strncpy(buffer, templates_xten_delim[j], at_offset);
1174 buffer[at_offset] = '\0';
1175 strcat(buffer, urls[i].text);
1176 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1177 end_offset = at_offset + strlen(urls[i].text);
1179 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1180 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1182 /* This assumes no templates start with the URL itself, and that they
1183 have at least two characters before the URL text */
1184 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1185 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1186 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1187 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1188 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1189 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1193 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1194 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1195 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1196 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1197 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1198 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1202 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1203 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1204 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1205 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1206 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1207 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1209 if (buffer[end_offset +1] != '\0')
1211 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1212 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1213 if (buffer[end_offset +2] != '\0')
1215 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1216 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1221 DestroyWindow(hwndRichEdit);
1222 hwndRichEdit = NULL;
1225 DestroyWindow(parent);
1228 static void test_EM_SCROLL(void)
1231 int r; /* return value */
1232 int expr; /* expected return value */
1233 HWND hwndRichEdit = new_richedit(NULL);
1234 int y_before, y_after; /* units of lines of text */
1236 /* test a richedit box containing a single line of text */
1237 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
1239 for (i = 0; i < 4; i++) {
1240 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
1242 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
1243 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1244 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
1245 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
1246 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
1247 "(i == %d)\n", y_after, i);
1251 * test a richedit box that will scroll. There are two general
1252 * cases: the case without any long lines and the case with a long
1255 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
1257 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
1259 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
1260 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1261 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
1262 "LONG LINE \nb\nc\nd\ne");
1263 for (j = 0; j < 12; j++) /* reset scroll position to top */
1264 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
1266 /* get first visible line */
1267 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1268 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
1270 /* get new current first visible line */
1271 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1273 ok(((r & 0xffffff00) == 0x00010000) &&
1274 ((r & 0x000000ff) != 0x00000000),
1275 "EM_SCROLL page down didn't scroll by a small positive number of "
1276 "lines (r == 0x%08x)\n", r);
1277 ok(y_after > y_before, "EM_SCROLL page down not functioning "
1278 "(line %d scrolled to line %d\n", y_before, y_after);
1282 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
1283 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1284 ok(((r & 0xffffff00) == 0x0001ff00),
1285 "EM_SCROLL page up didn't scroll by a small negative number of lines "
1286 "(r == 0x%08x)\n", r);
1287 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
1288 "%d scrolled to line %d\n", y_before, y_after);
1292 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
1294 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1296 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
1297 "(r == 0x%08x)\n", r);
1298 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
1299 "1 line (%d scrolled to %d)\n", y_before, y_after);
1303 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
1305 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1307 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
1308 "(r == 0x%08x)\n", r);
1309 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
1310 "line (%d scrolled to %d)\n", y_before, y_after);
1314 r = SendMessage(hwndRichEdit, EM_SCROLL,
1315 SB_LINEUP, 0); /* lineup beyond top */
1317 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1320 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
1321 ok(y_before == y_after,
1322 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
1326 r = SendMessage(hwndRichEdit, EM_SCROLL,
1327 SB_PAGEUP, 0);/*page up beyond top */
1329 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1332 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
1333 ok(y_before == y_after,
1334 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
1336 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
1337 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
1338 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1339 r = SendMessage(hwndRichEdit, EM_SCROLL,
1340 SB_PAGEDOWN, 0); /* page down beyond bot */
1341 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1344 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
1345 ok(y_before == y_after,
1346 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
1349 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1350 SendMessage(hwndRichEdit, EM_SCROLL,
1351 SB_LINEDOWN, 0); /* line down beyond bot */
1352 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
1355 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
1356 ok(y_before == y_after,
1357 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
1360 DestroyWindow(hwndRichEdit);
1363 static void test_EM_SETUNDOLIMIT(void)
1365 /* cases we test for:
1366 * default behaviour - limiting at 100 undo's
1367 * undo disabled - setting a limit of 0
1368 * undo limited - undo limit set to some to some number, like 2
1369 * bad input - sending a negative number should default to 100 undo's */
1371 HWND hwndRichEdit = new_richedit(NULL);
1376 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1379 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1380 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
1381 also, multiple pastes don't combine like WM_CHAR would */
1382 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1384 /* first case - check the default */
1385 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1386 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
1387 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1388 for (i=0; i<100; i++) /* Undo 100 of them */
1389 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1390 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1391 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
1393 /* second case - cannot undo */
1394 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
1395 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
1396 SendMessage(hwndRichEdit,
1397 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
1398 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1399 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
1401 /* third case - set it to an arbitrary number */
1402 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
1403 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
1404 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1405 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1406 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1407 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
1408 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
1409 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
1410 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1411 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1412 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
1413 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1414 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
1415 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
1417 /* fourth case - setting negative numbers should default to 100 undos */
1418 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1419 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
1421 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
1423 DestroyWindow(hwndRichEdit);
1426 static void test_ES_PASSWORD(void)
1428 /* This isn't hugely testable, so we're just going to run it through its paces */
1430 HWND hwndRichEdit = new_richedit(NULL);
1433 /* First, check the default of a regular control */
1434 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1436 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
1438 /* Now, set it to something normal */
1439 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
1440 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1442 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1444 /* Now, set it to something odd */
1445 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
1446 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
1448 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
1449 DestroyWindow(hwndRichEdit);
1452 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
1457 char** str = (char**)dwCookie;
1460 memcpy(*str, pbBuff, *pcb);
1466 static void test_WM_SETTEXT()
1468 HWND hwndRichEdit = new_richedit(NULL);
1469 const char * TestItem1 = "TestSomeText";
1470 const char * TestItem2 = "TestSomeText\r";
1471 const char * TestItem2_after = "TestSomeText\r\n";
1472 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
1473 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
1474 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
1475 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
1476 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
1477 const char * TestItem5_after = "TestSomeText TestSomeText";
1478 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
1479 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
1480 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
1481 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
1483 char buf[1024] = {0};
1488 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1489 any solitary \r to be converted to \r\n on return. Properly paired
1490 \r\n are not affected. It also shows that the special sequence \r\r\n
1491 gets converted to a single space.
1494 #define TEST_SETTEXT(a, b) \
1495 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
1496 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
1497 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
1498 ok (result == lstrlen(buf), \
1499 "WM_GETTEXT returned %ld instead of expected %u\n", \
1500 result, lstrlen(buf)); \
1501 result = strcmp(b, buf); \
1503 "WM_SETTEXT round trip: strcmp = %ld\n", result);
1505 TEST_SETTEXT(TestItem1, TestItem1)
1506 TEST_SETTEXT(TestItem2, TestItem2_after)
1507 TEST_SETTEXT(TestItem3, TestItem3_after)
1508 TEST_SETTEXT(TestItem3_after, TestItem3_after)
1509 TEST_SETTEXT(TestItem4, TestItem4_after)
1510 TEST_SETTEXT(TestItem5, TestItem5_after)
1511 TEST_SETTEXT(TestItem6, TestItem6_after)
1512 TEST_SETTEXT(TestItem7, TestItem7_after)
1514 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
1515 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1517 es.dwCookie = (DWORD_PTR)&p;
1519 es.pfnCallback = test_WM_SETTEXT_esCallback;
1520 memset(buf, 0, sizeof(buf));
1521 SendMessage(hwndRichEdit, EM_STREAMOUT,
1522 (WPARAM)(SF_RTF), (LPARAM)&es);
1523 trace("EM_STREAMOUT produced: \n%s\n", buf);
1524 TEST_SETTEXT(buf, TestItem1)
1527 DestroyWindow(hwndRichEdit);
1530 static void test_EM_STREAMOUT(void)
1532 HWND hwndRichEdit = new_richedit(NULL);
1535 char buf[1024] = {0};
1538 const char * TestItem1 = "TestSomeText";
1539 const char * TestItem2 = "TestSomeText\r";
1540 const char * TestItem3 = "TestSomeText\r\n";
1542 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1544 es.dwCookie = (DWORD_PTR)&p;
1546 es.pfnCallback = test_WM_SETTEXT_esCallback;
1547 memset(buf, 0, sizeof(buf));
1548 SendMessage(hwndRichEdit, EM_STREAMOUT,
1549 (WPARAM)(SF_TEXT), (LPARAM)&es);
1551 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
1552 ok(strcmp(buf, TestItem1) == 0,
1553 "streamed text different, got %s\n", buf);
1555 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
1557 es.dwCookie = (DWORD_PTR)&p;
1559 es.pfnCallback = test_WM_SETTEXT_esCallback;
1560 memset(buf, 0, sizeof(buf));
1561 SendMessage(hwndRichEdit, EM_STREAMOUT,
1562 (WPARAM)(SF_TEXT), (LPARAM)&es);
1564 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
1565 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
1566 ok(strcmp(buf, TestItem3) == 0,
1567 "streamed text different from, got %s\n", buf);
1568 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
1570 es.dwCookie = (DWORD_PTR)&p;
1572 es.pfnCallback = test_WM_SETTEXT_esCallback;
1573 memset(buf, 0, sizeof(buf));
1574 SendMessage(hwndRichEdit, EM_STREAMOUT,
1575 (WPARAM)(SF_TEXT), (LPARAM)&es);
1577 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
1578 ok(strcmp(buf, TestItem3) == 0,
1579 "streamed text different, got %s\n", buf);
1581 DestroyWindow(hwndRichEdit);
1584 static void test_EM_SETTEXTEX(void)
1586 HWND hwndRichEdit = new_richedit(NULL);
1589 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1591 'T', 'e', 'x', 't', 0};
1592 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1596 const char * TestItem2_after = "TestSomeText\r\n";
1597 WCHAR TestItem3[] = {'T', 'e', 's', 't',
1600 '\r','\n','\r','\n', 0};
1601 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
1605 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
1609 WCHAR TestItem4[] = {'T', 'e', 's', 't',
1612 '\r','\r','\n','\r',
1614 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
1618 #define MAX_BUF_LEN 1024
1619 WCHAR buf[MAX_BUF_LEN];
1625 setText.codepage = 1200; /* no constant for unicode */
1626 getText.codepage = 1200; /* no constant for unicode */
1627 getText.cb = MAX_BUF_LEN;
1628 getText.flags = GT_DEFAULT;
1629 getText.lpDefaultChar = NULL;
1630 getText.lpUsedDefChar = NULL;
1633 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1634 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1635 ok(lstrcmpW(buf, TestItem1) == 0,
1636 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1638 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1639 convert \r to \r\n on return
1641 setText.codepage = 1200; /* no constant for unicode */
1642 getText.codepage = 1200; /* no constant for unicode */
1643 getText.cb = MAX_BUF_LEN;
1644 getText.flags = GT_DEFAULT;
1645 getText.lpDefaultChar = NULL;
1646 getText.lpUsedDefChar = NULL;
1648 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1649 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1650 ok(lstrcmpW(buf, TestItem2) == 0,
1651 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1653 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1654 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1655 ok(strcmp((const char *)buf, TestItem2_after) == 0,
1656 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1658 /* Baseline test for just-enough buffer space for string */
1659 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1660 getText.codepage = 1200; /* no constant for unicode */
1661 getText.flags = GT_DEFAULT;
1662 getText.lpDefaultChar = NULL;
1663 getText.lpUsedDefChar = NULL;
1664 memset(buf, 0, MAX_BUF_LEN);
1665 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1666 ok(lstrcmpW(buf, TestItem2) == 0,
1667 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1669 /* When there is enough space for one character, but not both, of the CRLF
1670 pair at the end of the string, the CR is not copied at all. That is,
1671 the caller must not see CRLF pairs truncated to CR at the end of the
1674 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1675 getText.codepage = 1200; /* no constant for unicode */
1676 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
1677 getText.lpDefaultChar = NULL;
1678 getText.lpUsedDefChar = NULL;
1679 memset(buf, 0, MAX_BUF_LEN);
1680 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1681 ok(lstrcmpW(buf, TestItem1) == 0,
1682 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1685 /* \r\n pairs get changed into \r */
1686 setText.codepage = 1200; /* no constant for unicode */
1687 getText.codepage = 1200; /* no constant for unicode */
1688 getText.cb = MAX_BUF_LEN;
1689 getText.flags = GT_DEFAULT;
1690 getText.lpDefaultChar = NULL;
1691 getText.lpUsedDefChar = NULL;
1693 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
1694 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1695 ok(lstrcmpW(buf, TestItem3_after) == 0,
1696 "EM_SETTEXTEX did not convert properly\n");
1698 /* \n also gets changed to \r */
1699 setText.codepage = 1200; /* no constant for unicode */
1700 getText.codepage = 1200; /* no constant for unicode */
1701 getText.cb = MAX_BUF_LEN;
1702 getText.flags = GT_DEFAULT;
1703 getText.lpDefaultChar = NULL;
1704 getText.lpUsedDefChar = NULL;
1706 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
1707 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1708 ok(lstrcmpW(buf, TestItem3_after) == 0,
1709 "EM_SETTEXTEX did not convert properly\n");
1711 /* \r\r\n gets changed into single space */
1712 setText.codepage = 1200; /* no constant for unicode */
1713 getText.codepage = 1200; /* no constant for unicode */
1714 getText.cb = MAX_BUF_LEN;
1715 getText.flags = GT_DEFAULT;
1716 getText.lpDefaultChar = NULL;
1717 getText.lpUsedDefChar = NULL;
1719 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
1720 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1721 ok(lstrcmpW(buf, TestItem4_after) == 0,
1722 "EM_SETTEXTEX did not convert properly\n");
1724 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1725 (WPARAM)&setText, (LPARAM) NULL);
1726 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1729 "EM_SETTEXTEX returned %d, instead of 1\n",result);
1730 ok(lstrlenW(buf) == 0,
1731 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1733 /* put some text back */
1735 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1736 /* select some text */
1739 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1740 /* replace current selection */
1741 setText.flags = ST_SELECTION;
1742 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1743 (WPARAM)&setText, (LPARAM) NULL);
1745 "EM_SETTEXTEX with NULL lParam to replace selection"
1746 " with no text should return 0. Got %i\n",
1749 /* put some text back */
1751 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1752 /* select some text */
1755 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1756 /* replace current selection */
1757 setText.flags = ST_SELECTION;
1758 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1759 (WPARAM)&setText, (LPARAM) TestItem1);
1761 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1762 ok(result == lstrlenW(TestItem1),
1763 "EM_SETTEXTEX with NULL lParam to replace selection"
1764 " with no text should return 0. Got %i\n",
1766 ok(lstrlenW(buf) == 22,
1767 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1770 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
1771 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
1773 es.dwCookie = (DWORD_PTR)&p;
1775 es.pfnCallback = test_WM_SETTEXT_esCallback;
1776 memset(buf, 0, sizeof(buf));
1777 SendMessage(hwndRichEdit, EM_STREAMOUT,
1778 (WPARAM)(SF_RTF), (LPARAM)&es);
1779 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
1781 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
1782 getText.codepage = 1200; /* no constant for unicode */
1783 getText.cb = MAX_BUF_LEN;
1784 getText.flags = GT_DEFAULT;
1785 getText.lpDefaultChar = NULL;
1786 getText.lpUsedDefChar = NULL;
1789 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
1790 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1791 ok(lstrcmpW(buf, TestItem1) == 0,
1792 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1795 DestroyWindow(hwndRichEdit);
1798 static void test_EM_LIMITTEXT(void)
1802 HWND hwndRichEdit = new_richedit(NULL);
1804 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1805 * about setting the length to -1 for multiline edit controls doesn't happen.
1808 /* Don't check default gettextlimit case. That's done in other tests */
1810 /* Set textlimit to 100 */
1811 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1812 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1814 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1816 /* Set textlimit to 0 */
1817 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1818 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1820 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1822 /* Set textlimit to -1 */
1823 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1824 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1826 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1828 /* Set textlimit to -2 */
1829 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1830 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1832 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1834 DestroyWindow (hwndRichEdit);
1838 static void test_EM_EXLIMITTEXT(void)
1840 int i, selBegin, selEnd, len1, len2;
1842 char text[1024 + 1];
1843 char buffer[1024 + 1];
1844 int textlimit = 0; /* multiple of 100 */
1845 HWND hwndRichEdit = new_richedit(NULL);
1847 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1848 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1851 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1852 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1854 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1857 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1858 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1860 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1862 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1863 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1864 /* default for WParam = 0 */
1865 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1867 textlimit = sizeof(text)-1;
1868 memset(text, 'W', textlimit);
1869 text[sizeof(text)-1] = 0;
1870 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1871 /* maxed out text */
1872 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1874 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1875 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1876 len1 = selEnd - selBegin;
1878 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1879 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1880 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1881 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1882 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1883 len2 = selEnd - selBegin;
1886 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1889 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1890 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1891 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1892 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1893 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1894 len1 = selEnd - selBegin;
1897 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1900 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1901 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1902 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1903 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1904 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1905 len2 = selEnd - selBegin;
1908 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1911 /* set text up to the limit, select all the text, then add a char */
1913 memset(text, 'W', textlimit);
1914 text[textlimit] = 0;
1915 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1916 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1917 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1918 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1919 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1920 result = strcmp(buffer, "A");
1921 ok(0 == result, "got string = \"%s\"\n", buffer);
1923 /* WM_SETTEXT not limited */
1925 memset(text, 'W', textlimit);
1926 text[textlimit] = 0;
1927 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1928 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1929 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1931 ok(10 == i, "expected 10 chars\n");
1932 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1933 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1935 /* try inserting more text at end */
1936 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1937 ok(0 == i, "WM_CHAR wasn't processed\n");
1938 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1940 ok(10 == i, "expected 10 chars, got %i\n", i);
1941 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1942 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1944 /* try inserting text at beginning */
1945 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1946 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1947 ok(0 == i, "WM_CHAR wasn't processed\n");
1948 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1950 ok(10 == i, "expected 10 chars, got %i\n", i);
1951 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1952 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1954 /* WM_CHAR is limited */
1956 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1957 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1958 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1959 ok(0 == i, "WM_CHAR wasn't processed\n");
1960 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1961 ok(0 == i, "WM_CHAR wasn't processed\n");
1962 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1964 ok(1 == i, "expected 1 chars, got %i instead\n", i);
1966 DestroyWindow(hwndRichEdit);
1969 static void test_EM_GETLIMITTEXT(void)
1972 HWND hwndRichEdit = new_richedit(NULL);
1974 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1975 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1977 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1978 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1979 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1981 DestroyWindow(hwndRichEdit);
1984 static void test_WM_SETFONT(void)
1986 /* There is no invalid input or error conditions for this function.
1987 * NULL wParam and lParam just fall back to their default values
1988 * It should be noted that even if you use a gibberish name for your fonts
1989 * here, it will still work because the name is stored. They will display as
1990 * System, but will report their name to be whatever they were created as */
1992 HWND hwndRichEdit = new_richedit(NULL);
1993 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1994 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1995 FF_DONTCARE, "Marlett");
1996 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1997 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1998 FF_DONTCARE, "MS Sans Serif");
1999 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2000 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2001 FF_DONTCARE, "Courier");
2002 LOGFONTA sentLogFont;
2003 CHARFORMAT2A returnedCF2A;
2005 returnedCF2A.cbSize = sizeof(returnedCF2A);
2007 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2008 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
2009 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2011 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
2012 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2013 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
2014 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2016 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
2017 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2018 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
2019 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2020 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
2021 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2023 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
2024 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2025 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
2026 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2027 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
2028 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2030 /* This last test is special since we send in NULL. We clear the variables
2031 * and just compare to "System" instead of the sent in font name. */
2032 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
2033 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
2034 returnedCF2A.cbSize = sizeof(returnedCF2A);
2036 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
2037 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2038 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
2039 ok (!strcmp("System",returnedCF2A.szFaceName),
2040 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
2042 DestroyWindow(hwndRichEdit);
2046 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
2051 const char** str = (const char**)dwCookie;
2052 int size = strlen(*str);
2053 if(size > 3) /* let's make it piecemeal for fun */
2060 memcpy(pbBuff, *str, *pcb);
2066 static void test_EM_GETMODIFY(void)
2068 HWND hwndRichEdit = new_richedit(NULL);
2071 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2073 'T', 'e', 'x', 't', 0};
2074 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2076 'O', 't', 'h', 'e', 'r',
2077 'T', 'e', 'x', 't', 0};
2078 const char* streamText = "hello world";
2083 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2084 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2085 FF_DONTCARE, "Courier");
2087 setText.codepage = 1200; /* no constant for unicode */
2088 setText.flags = ST_KEEPUNDO;
2091 /* modify flag shouldn't be set when richedit is first created */
2092 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2094 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
2096 /* setting modify flag should actually set it */
2097 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
2098 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2100 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
2102 /* clearing modify flag should actually clear it */
2103 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2104 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2106 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
2108 /* setting font doesn't change modify flag */
2109 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2110 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
2111 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2113 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
2115 /* setting text should set modify flag */
2116 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2117 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2118 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2120 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
2122 /* undo previous text doesn't reset modify flag */
2123 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2124 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2126 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
2128 /* set text with no flag to keep undo stack should not set modify flag */
2129 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2131 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2132 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2134 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
2136 /* WM_SETTEXT doesn't modify */
2137 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2138 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
2139 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2141 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
2143 /* clear the text */
2144 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2145 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
2146 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2148 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
2151 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2152 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2153 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2154 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
2155 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2157 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
2159 /* copy/paste text 1 */
2160 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2161 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2162 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2163 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2164 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2166 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
2168 /* copy/paste text 2 */
2169 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2170 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2171 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2172 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
2173 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2174 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2176 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
2179 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2180 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
2181 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2182 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2184 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
2187 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2188 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2189 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
2190 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2192 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
2194 /* set char format */
2195 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2196 cf2.cbSize = sizeof(CHARFORMAT2);
2197 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
2199 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
2200 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
2201 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
2202 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
2203 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
2204 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2206 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
2208 /* set para format */
2209 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2210 pf2.cbSize = sizeof(PARAFORMAT2);
2211 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
2213 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
2214 pf2.wAlignment = PFA_RIGHT;
2215 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
2216 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2218 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
2221 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2222 es.dwCookie = (DWORD_PTR)&streamText;
2224 es.pfnCallback = test_EM_GETMODIFY_esCallback;
2225 SendMessage(hwndRichEdit, EM_STREAMIN,
2226 (WPARAM)(SF_TEXT), (LPARAM)&es);
2227 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2229 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
2231 DestroyWindow(hwndRichEdit);
2237 long expected_retval;
2238 int expected_getsel_start;
2239 int expected_getsel_end;
2240 int _exsetsel_todo_wine;
2241 int _getsel_todo_wine;
2244 const struct exsetsel_s exsetsel_tests[] = {
2246 {5, 10, 10, 5, 10, 0, 0},
2247 {15, 17, 17, 15, 17, 0, 0},
2248 /* test cpMax > strlen() */
2249 {0, 100, 18, 0, 18, 0, 1},
2250 /* test cpMin == cpMax */
2251 {5, 5, 5, 5, 5, 0, 0},
2252 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
2253 {-1, 0, 5, 5, 5, 0, 0},
2254 {-1, 17, 5, 5, 5, 0, 0},
2255 {-1, 18, 5, 5, 5, 0, 0},
2256 /* test cpMin < 0 && cpMax < 0 */
2257 {-1, -1, 17, 17, 17, 0, 0},
2258 {-4, -5, 17, 17, 17, 0, 0},
2259 /* test cMin >=0 && cpMax < 0 (bug 6814) */
2260 {0, -1, 18, 0, 18, 0, 1},
2261 {17, -5, 18, 17, 18, 0, 1},
2262 {18, -3, 17, 17, 17, 0, 0},
2263 /* test if cpMin > cpMax */
2264 {15, 19, 18, 15, 18, 0, 1},
2265 {19, 15, 18, 15, 18, 0, 1}
2268 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
2273 cr.cpMin = setsel->min;
2274 cr.cpMax = setsel->max;
2275 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
2277 if (setsel->_exsetsel_todo_wine) {
2279 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2282 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
2285 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
2287 if (setsel->_getsel_todo_wine) {
2289 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);
2292 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);
2296 static void test_EM_EXSETSEL(void)
2298 HWND hwndRichEdit = new_richedit(NULL);
2300 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
2302 /* sending some text to the window */
2303 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2304 /* 01234567890123456*/
2307 for (i = 0; i < num_tests; i++) {
2308 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
2311 DestroyWindow(hwndRichEdit);
2314 static void test_EM_REPLACESEL(int redraw)
2316 HWND hwndRichEdit = new_richedit(NULL);
2317 char buffer[1024] = {0};
2322 /* sending some text to the window */
2323 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
2324 /* 01234567890123456*/
2327 /* FIXME add more tests */
2328 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
2329 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
2330 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
2331 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2332 r = strcmp(buffer, "testing");
2333 ok(0 == r, "expected %d, got %d\n", 0, r);
2335 DestroyWindow(hwndRichEdit);
2337 hwndRichEdit = new_richedit(NULL);
2339 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
2340 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
2342 /* Test behavior with carriage returns and newlines */
2343 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2344 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
2345 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
2346 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2347 r = strcmp(buffer, "RichEdit1");
2348 ok(0 == r, "expected %d, got %d\n", 0, r);
2350 getText.codepage = CP_ACP;
2351 getText.flags = GT_DEFAULT;
2352 getText.lpDefaultChar = NULL;
2353 getText.lpUsedDefChar = NULL;
2354 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2355 ok(strcmp(buffer, "RichEdit1") == 0,
2356 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
2358 /* Test number of lines reported after EM_REPLACESEL */
2359 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2360 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2362 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2363 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
2364 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
2365 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2366 r = strcmp(buffer, "RichEdit1\r\n");
2367 ok(0 == r, "expected %d, got %d\n", 0, r);
2369 getText.codepage = CP_ACP;
2370 getText.flags = GT_DEFAULT;
2371 getText.lpDefaultChar = NULL;
2372 getText.lpUsedDefChar = NULL;
2373 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2374 ok(strcmp(buffer, "RichEdit1\r") == 0,
2375 "EM_GETTEXTEX returned incorrect string\n");
2377 /* Test number of lines reported after EM_REPLACESEL */
2378 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2379 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2381 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
2382 EM_REPLACESEL. The general rule seems to be that Win98's riched20
2383 returns the number of characters *inserted* into the control (after
2384 required conversions), but WinXP's riched20 returns the number of
2385 characters interpreted from the original lParam. Wine's builtin riched20
2386 implements the WinXP behavior.
2388 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2389 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
2390 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
2391 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
2393 /* Test number of lines reported after EM_REPLACESEL */
2394 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2395 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2397 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2398 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2399 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2400 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2402 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2403 r = strcmp(buffer, "RichEdit1\r\n");
2404 ok(0 == r, "expected %d, got %d\n", 0, r);
2406 getText.codepage = CP_ACP;
2407 getText.flags = GT_DEFAULT;
2408 getText.lpDefaultChar = NULL;
2409 getText.lpUsedDefChar = NULL;
2410 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2411 ok(strcmp(buffer, "RichEdit1\r") == 0,
2412 "EM_GETTEXTEX returned incorrect string\n");
2414 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2415 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2416 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
2417 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
2419 /* The following tests show that richedit should handle the special \r\r\n
2420 sequence by turning it into a single space on insertion. However,
2421 EM_REPLACESEL on WinXP returns the number of characters in the original
2425 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2426 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
2427 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
2428 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2429 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2430 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2431 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2433 /* Test the actual string */
2435 getText.codepage = CP_ACP;
2436 getText.flags = GT_DEFAULT;
2437 getText.lpDefaultChar = NULL;
2438 getText.lpUsedDefChar = NULL;
2439 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2440 ok(strcmp(buffer, "\r\r") == 0,
2441 "EM_GETTEXTEX returned incorrect string\n");
2443 /* Test number of lines reported after EM_REPLACESEL */
2444 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2445 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2447 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2448 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
2449 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
2450 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
2451 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2452 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2453 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
2454 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
2456 /* Test the actual string */
2458 getText.codepage = CP_ACP;
2459 getText.flags = GT_DEFAULT;
2460 getText.lpDefaultChar = NULL;
2461 getText.lpUsedDefChar = NULL;
2462 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2463 ok(strcmp(buffer, " ") == 0,
2464 "EM_GETTEXTEX returned incorrect string\n");
2466 /* Test number of lines reported after EM_REPLACESEL */
2467 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2468 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
2470 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2471 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
2472 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2473 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2474 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2475 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2476 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2477 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2479 /* Test the actual string */
2481 getText.codepage = CP_ACP;
2482 getText.flags = GT_DEFAULT;
2483 getText.lpDefaultChar = NULL;
2484 getText.lpUsedDefChar = NULL;
2485 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2486 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
2487 "EM_GETTEXTEX returned incorrect string\n");
2489 /* Test number of lines reported after EM_REPLACESEL */
2490 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2491 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2493 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2494 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
2495 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
2496 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
2497 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2498 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2499 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2500 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2502 /* Test the actual string */
2504 getText.codepage = CP_ACP;
2505 getText.flags = GT_DEFAULT;
2506 getText.lpDefaultChar = NULL;
2507 getText.lpUsedDefChar = NULL;
2508 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2509 ok(strcmp(buffer, " \r") == 0,
2510 "EM_GETTEXTEX returned incorrect string\n");
2512 /* Test number of lines reported after EM_REPLACESEL */
2513 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2514 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
2516 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2517 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
2518 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
2519 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
2520 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2521 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2522 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
2523 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
2525 /* Test the actual string */
2527 getText.codepage = CP_ACP;
2528 getText.flags = GT_DEFAULT;
2529 getText.lpDefaultChar = NULL;
2530 getText.lpUsedDefChar = NULL;
2531 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2532 ok(strcmp(buffer, " \r\r") == 0,
2533 "EM_GETTEXTEX returned incorrect string\n");
2535 /* Test number of lines reported after EM_REPLACESEL */
2536 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2537 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2539 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2540 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
2541 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
2542 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
2543 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2544 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2545 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
2546 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
2548 /* Test the actual string */
2550 getText.codepage = CP_ACP;
2551 getText.flags = GT_DEFAULT;
2552 getText.lpDefaultChar = NULL;
2553 getText.lpUsedDefChar = NULL;
2554 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2555 ok(strcmp(buffer, "\rX\r\r\r") == 0,
2556 "EM_GETTEXTEX returned incorrect string\n");
2558 /* Test number of lines reported after EM_REPLACESEL */
2559 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2560 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
2562 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2563 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
2564 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
2565 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2566 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2567 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
2568 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
2570 /* Test the actual string */
2572 getText.codepage = CP_ACP;
2573 getText.flags = GT_DEFAULT;
2574 getText.lpDefaultChar = NULL;
2575 getText.lpUsedDefChar = NULL;
2576 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2577 ok(strcmp(buffer, "\r\r") == 0,
2578 "EM_GETTEXTEX returned incorrect string\n");
2580 /* Test number of lines reported after EM_REPLACESEL */
2581 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2582 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
2584 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
2585 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
2586 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
2587 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
2588 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
2589 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
2590 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
2591 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
2593 /* Test the actual string */
2595 getText.codepage = CP_ACP;
2596 getText.flags = GT_DEFAULT;
2597 getText.lpDefaultChar = NULL;
2598 getText.lpUsedDefChar = NULL;
2599 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
2600 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
2601 "EM_GETTEXTEX returned incorrect string\n");
2603 /* Test number of lines reported after EM_REPLACESEL */
2604 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
2605 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
2607 DestroyWindow(hwndRichEdit);
2610 static void test_WM_PASTE(void)
2614 char buffer[1024] = {0};
2615 char key_info[][3] =
2617 /* VirtualKey, ScanCode, WM_CHAR code */
2618 {'C', 0x2e, 3}, /* Ctrl-C */
2619 {'X', 0x2d, 24}, /* Ctrl-X */
2620 {'V', 0x2f, 22}, /* Ctrl-V */
2621 {'Z', 0x2c, 26}, /* Ctrl-Z */
2622 {'Y', 0x15, 25}, /* Ctrl-Y */
2624 const char* text1 = "testing paste\r";
2625 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
2626 const char* text1_after = "testing paste\r\n";
2627 const char* text2 = "testing paste\r\rtesting paste";
2628 const char* text2_after = "testing paste\r\n\r\ntesting paste";
2629 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
2630 HWND hwndRichEdit = new_richedit(NULL);
2632 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
2633 messages, probably because it inspects the keyboard state itself.
2634 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
2636 #define SEND_CTRL_KEY(hwnd, k) \
2637 keybd_event(VK_CONTROL, 0x1d, 0, 0);\
2638 keybd_event(k[0], k[1], 0, 0);\
2639 keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
2640 keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
2641 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
2642 TranslateMessage(&msg); \
2643 DispatchMessage(&msg); \
2646 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
2647 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
2648 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
2649 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
2650 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
2652 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
2653 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
2655 SEND_CTRL_C(hwndRichEdit) /* Copy */
2656 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2657 SEND_CTRL_V(hwndRichEdit) /* Paste */
2658 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2659 /* Pasted text should be visible at this step */
2660 result = strcmp(text1_step1, buffer);
2662 "test paste: strcmp = %i\n", result);
2663 SEND_CTRL_Z(hwndRichEdit) /* Undo */
2664 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2665 /* Text should be the same as before (except for \r -> \r\n conversion) */
2666 result = strcmp(text1_after, buffer);
2668 "test paste: strcmp = %i\n", result);
2670 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
2671 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
2672 SEND_CTRL_C(hwndRichEdit) /* Copy */
2673 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2674 SEND_CTRL_V(hwndRichEdit) /* Paste */
2675 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2676 /* Pasted text should be visible at this step */
2677 result = strcmp(text3, buffer);
2679 "test paste: strcmp = %i\n", result);
2680 SEND_CTRL_Z(hwndRichEdit) /* Undo */
2681 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2682 /* Text should be the same as before (except for \r -> \r\n conversion) */
2683 result = strcmp(text2_after, buffer);
2685 "test paste: strcmp = %i\n", result);
2686 SEND_CTRL_Y(hwndRichEdit) /* Redo */
2687 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2688 /* Text should revert to post-paste state */
2689 result = strcmp(buffer,text3);
2691 "test paste: strcmp = %i\n", result);
2693 DestroyWindow(hwndRichEdit);
2696 static void test_EM_FORMATRANGE(void)
2701 HWND hwndRichEdit = new_richedit(NULL);
2703 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
2705 hdc = GetDC(hwndRichEdit);
2706 ok(hdc != NULL, "Could not get HDC\n");
2708 fr.hdc = fr.hdcTarget = hdc;
2709 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
2710 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
2711 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
2715 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2717 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2720 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2722 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
2728 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2730 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
2733 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2735 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2738 DestroyWindow(hwndRichEdit);
2741 static int nCallbackCount = 0;
2743 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
2746 const char text[] = {'t','e','s','t'};
2748 if (sizeof(text) <= cb)
2750 if ((int)dwCookie != nCallbackCount)
2756 memcpy (pbBuff, text, sizeof(text));
2757 *pcb = sizeof(text);
2764 return 1; /* indicates callback failed */
2767 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
2772 const char** str = (const char**)dwCookie;
2773 int size = strlen(*str);
2779 memcpy(pbBuff, *str, *pcb);
2786 static void test_EM_STREAMIN(void)
2788 HWND hwndRichEdit = new_richedit(NULL);
2791 char buffer[1024] = {0};
2793 const char * streamText0 = "{\\rtf1 TestSomeText}";
2794 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
2795 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
2797 const char * streamText1 =
2798 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
2799 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
2802 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
2803 const char * streamText2 =
2804 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
2805 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
2806 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
2807 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
2808 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
2809 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
2810 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
2812 const char * streamText3 = "RichEdit1";
2814 /* Minimal test without \par at the end */
2815 es.dwCookie = (DWORD_PTR)&streamText0;
2817 es.pfnCallback = test_EM_STREAMIN_esCallback;
2818 SendMessage(hwndRichEdit, EM_STREAMIN,
2819 (WPARAM)(SF_RTF), (LPARAM)&es);
2821 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2823 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
2824 result = strcmp (buffer,"TestSomeText");
2826 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
2827 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
2829 /* Native richedit 2.0 ignores last \par */
2830 es.dwCookie = (DWORD_PTR)&streamText0a;
2832 es.pfnCallback = test_EM_STREAMIN_esCallback;
2833 SendMessage(hwndRichEdit, EM_STREAMIN,
2834 (WPARAM)(SF_RTF), (LPARAM)&es);
2836 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2838 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
2839 result = strcmp (buffer,"TestSomeText");
2841 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
2842 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
2844 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
2845 es.dwCookie = (DWORD_PTR)&streamText0b;
2847 es.pfnCallback = test_EM_STREAMIN_esCallback;
2848 SendMessage(hwndRichEdit, EM_STREAMIN,
2849 (WPARAM)(SF_RTF), (LPARAM)&es);
2851 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2853 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
2854 result = strcmp (buffer,"TestSomeText\r\n");
2856 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
2857 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
2859 es.dwCookie = (DWORD_PTR)&streamText1;
2861 es.pfnCallback = test_EM_STREAMIN_esCallback;
2862 SendMessage(hwndRichEdit, EM_STREAMIN,
2863 (WPARAM)(SF_RTF), (LPARAM)&es);
2865 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2867 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
2868 result = strcmp (buffer,"TestSomeText");
2870 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2871 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
2873 es.dwCookie = (DWORD_PTR)&streamText2;
2875 SendMessage(hwndRichEdit, EM_STREAMIN,
2876 (WPARAM)(SF_RTF), (LPARAM)&es);
2878 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2880 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
2881 ok (strlen(buffer) == 0,
2882 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2883 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
2885 es.dwCookie = (DWORD_PTR)&streamText3;
2887 SendMessage(hwndRichEdit, EM_STREAMIN,
2888 (WPARAM)(SF_RTF), (LPARAM)&es);
2890 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2892 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
2893 ok (strlen(buffer) == 0,
2894 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
2895 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
2897 DestroyWindow(hwndRichEdit);
2900 static void test_EM_StreamIn_Undo(void)
2902 /* The purpose of this test is to determine when a EM_StreamIn should be
2903 * undoable. This is important because WM_PASTE currently uses StreamIn and
2904 * pasting should always be undoable but streaming isn't always.
2907 * StreamIn plain text without SFF_SELECTION.
2908 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
2909 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
2910 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
2911 * Feel free to add tests for other text modes or StreamIn things.
2915 HWND hwndRichEdit = new_richedit(NULL);
2918 char buffer[1024] = {0};
2919 const char randomtext[] = "Some text";
2921 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
2923 /* StreamIn, no SFF_SELECTION */
2924 es.dwCookie = nCallbackCount;
2925 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2926 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2927 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2928 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
2929 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2930 result = strcmp (buffer,"test");
2932 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2934 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2935 ok (result == FALSE,
2936 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
2938 /* StreamIn, SFF_SELECTION, but nothing selected */
2939 es.dwCookie = nCallbackCount;
2940 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2941 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2942 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2943 SendMessage(hwndRichEdit, EM_STREAMIN,
2944 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2945 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2946 result = strcmp (buffer,"testSome text");
2948 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2950 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2952 "EM_STREAMIN with SFF_SELECTION but no selection set "
2953 "should create an undo\n");
2955 /* StreamIn, SFF_SELECTION, with a selection */
2956 es.dwCookie = nCallbackCount;
2957 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2958 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2959 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
2960 SendMessage(hwndRichEdit, EM_STREAMIN,
2961 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2962 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2963 result = strcmp (buffer,"Sometesttext");
2965 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2967 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2969 "EM_STREAMIN with SFF_SELECTION and selection set "
2970 "should create an undo\n");
2974 static BOOL is_em_settextex_supported(HWND hwnd)
2976 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
2977 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
2980 static void test_unicode_conversions(void)
2982 static const WCHAR tW[] = {'t',0};
2983 static const WCHAR teW[] = {'t','e',0};
2984 static const WCHAR textW[] = {'t','e','s','t',0};
2985 static const char textA[] = "test";
2989 int is_win9x, em_settextex_supported, ret;
2991 is_win9x = GetVersion() & 0x80000000;
2993 #define set_textA(hwnd, wm_set_text, txt) \
2995 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
2996 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2997 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2998 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2999 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
3001 #define expect_textA(hwnd, wm_get_text, txt) \
3003 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3004 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
3005 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3006 memset(bufA, 0xAA, sizeof(bufA)); \
3007 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3008 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3009 ret = lstrcmpA(bufA, txt); \
3010 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
3013 #define set_textW(hwnd, wm_set_text, txt) \
3015 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
3016 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3017 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3018 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3019 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
3021 #define expect_textW(hwnd, wm_get_text, txt) \
3023 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
3024 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
3025 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3026 memset(bufW, 0xAA, sizeof(bufW)); \
3029 assert(wm_get_text == EM_GETTEXTEX); \
3030 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3031 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3035 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3036 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
3038 ret = lstrcmpW(bufW, txt); \
3039 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
3041 #define expect_empty(hwnd, wm_get_text) \
3043 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3044 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
3045 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3046 memset(bufA, 0xAA, sizeof(bufA)); \
3047 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3048 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
3049 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
3052 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3053 0, 0, 200, 60, 0, 0, 0, 0);
3054 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3056 ret = IsWindowUnicode(hwnd);
3058 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
3060 ok(ret, "RichEdit20W should be unicode under NT\n");
3062 /* EM_SETTEXTEX is supported starting from version 3.0 */
3063 em_settextex_supported = is_em_settextex_supported(hwnd);
3064 trace("EM_SETTEXTEX is %ssupported on this platform\n",
3065 em_settextex_supported ? "" : "NOT ");
3067 expect_empty(hwnd, WM_GETTEXT);
3068 expect_empty(hwnd, EM_GETTEXTEX);
3070 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
3071 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3072 expect_textA(hwnd, WM_GETTEXT, "t");
3073 expect_textA(hwnd, EM_GETTEXTEX, "t");
3074 expect_textW(hwnd, EM_GETTEXTEX, tW);
3076 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
3077 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3078 expect_textA(hwnd, WM_GETTEXT, "te");
3079 expect_textA(hwnd, EM_GETTEXTEX, "te");
3080 expect_textW(hwnd, EM_GETTEXTEX, teW);
3082 set_textA(hwnd, WM_SETTEXT, NULL);
3083 expect_empty(hwnd, WM_GETTEXT);
3084 expect_empty(hwnd, EM_GETTEXTEX);
3087 set_textA(hwnd, WM_SETTEXT, textW);
3089 set_textA(hwnd, WM_SETTEXT, textA);
3090 expect_textA(hwnd, WM_GETTEXT, textA);
3091 expect_textA(hwnd, EM_GETTEXTEX, textA);
3092 expect_textW(hwnd, EM_GETTEXTEX, textW);
3094 if (em_settextex_supported)
3096 set_textA(hwnd, EM_SETTEXTEX, textA);
3097 expect_textA(hwnd, WM_GETTEXT, textA);
3098 expect_textA(hwnd, EM_GETTEXTEX, textA);
3099 expect_textW(hwnd, EM_GETTEXTEX, textW);
3104 set_textW(hwnd, WM_SETTEXT, textW);
3105 expect_textW(hwnd, WM_GETTEXT, textW);
3106 expect_textA(hwnd, WM_GETTEXT, textA);
3107 expect_textW(hwnd, EM_GETTEXTEX, textW);
3108 expect_textA(hwnd, EM_GETTEXTEX, textA);
3110 if (em_settextex_supported)
3112 set_textW(hwnd, EM_SETTEXTEX, textW);
3113 expect_textW(hwnd, WM_GETTEXT, textW);
3114 expect_textA(hwnd, WM_GETTEXT, textA);
3115 expect_textW(hwnd, EM_GETTEXTEX, textW);
3116 expect_textA(hwnd, EM_GETTEXTEX, textA);
3119 DestroyWindow(hwnd);
3121 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
3122 0, 0, 200, 60, 0, 0, 0, 0);
3123 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3125 ret = IsWindowUnicode(hwnd);
3126 ok(!ret, "RichEdit20A should NOT be unicode\n");
3128 set_textA(hwnd, WM_SETTEXT, textA);
3129 expect_textA(hwnd, WM_GETTEXT, textA);
3130 expect_textA(hwnd, EM_GETTEXTEX, textA);
3131 expect_textW(hwnd, EM_GETTEXTEX, textW);
3133 if (em_settextex_supported)
3135 set_textA(hwnd, EM_SETTEXTEX, textA);
3136 expect_textA(hwnd, WM_GETTEXT, textA);
3137 expect_textA(hwnd, EM_GETTEXTEX, textA);
3138 expect_textW(hwnd, EM_GETTEXTEX, textW);
3143 set_textW(hwnd, WM_SETTEXT, textW);
3144 expect_textW(hwnd, WM_GETTEXT, textW);
3145 expect_textA(hwnd, WM_GETTEXT, textA);
3146 expect_textW(hwnd, EM_GETTEXTEX, textW);
3147 expect_textA(hwnd, EM_GETTEXTEX, textA);
3149 if (em_settextex_supported)
3151 set_textW(hwnd, EM_SETTEXTEX, textW);
3152 expect_textW(hwnd, WM_GETTEXT, textW);
3153 expect_textA(hwnd, WM_GETTEXT, textA);
3154 expect_textW(hwnd, EM_GETTEXTEX, textW);
3155 expect_textA(hwnd, EM_GETTEXTEX, textA);
3158 DestroyWindow(hwnd);
3161 static void test_WM_CHAR(void)
3165 const char * char_list = "abc\rabc\r";
3166 const char * expected_content_single = "abcabc";
3167 const char * expected_content_multi = "abc\r\nabc\r\n";
3168 char buffer[64] = {0};
3171 /* single-line control must IGNORE carriage returns */
3172 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3173 0, 0, 200, 60, 0, 0, 0, 0);
3174 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3177 while (*p != '\0') {
3178 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
3179 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
3180 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
3181 SendMessageA(hwnd, WM_KEYUP, *p, 1);
3185 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3186 ret = strcmp(buffer, expected_content_single);
3187 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3189 DestroyWindow(hwnd);
3191 /* multi-line control inserts CR normally */
3192 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
3193 0, 0, 200, 60, 0, 0, 0, 0);
3194 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3197 while (*p != '\0') {
3198 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
3199 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
3200 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
3201 SendMessageA(hwnd, WM_KEYUP, *p, 1);
3205 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3206 ret = strcmp(buffer, expected_content_multi);
3207 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3209 DestroyWindow(hwnd);
3212 static void test_EM_GETTEXTLENGTHEX(void)
3215 GETTEXTLENGTHEX gtl;
3217 const char * base_string = "base string";
3218 const char * test_string = "a\nb\n\n\r\n";
3219 const char * test_string_after = "a";
3220 const char * test_string_2 = "a\rtest\rstring";
3221 char buffer[64] = {0};
3224 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3225 0, 0, 200, 60, 0, 0, 0, 0);
3226 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3228 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3229 gtl.codepage = CP_ACP;
3230 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3231 ok(ret == 0, "ret %d\n",ret);
3233 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3234 gtl.codepage = CP_ACP;
3235 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3236 ok(ret == 0, "ret %d\n",ret);
3238 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
3240 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3241 gtl.codepage = CP_ACP;
3242 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3243 ok(ret == strlen(base_string), "ret %d\n",ret);
3245 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3246 gtl.codepage = CP_ACP;
3247 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3248 ok(ret == strlen(base_string), "ret %d\n",ret);
3250 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3252 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3253 gtl.codepage = CP_ACP;
3254 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3255 ok(ret == 1, "ret %d\n",ret);
3257 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3258 gtl.codepage = CP_ACP;
3259 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3260 ok(ret == 1, "ret %d\n",ret);
3262 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3263 ret = strcmp(buffer, test_string_after);
3264 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3266 DestroyWindow(hwnd);
3269 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
3270 0, 0, 200, 60, 0, 0, 0, 0);
3271 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3273 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3274 gtl.codepage = CP_ACP;
3275 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3276 ok(ret == 0, "ret %d\n",ret);
3278 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3279 gtl.codepage = CP_ACP;
3280 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3281 ok(ret == 0, "ret %d\n",ret);
3283 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
3285 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3286 gtl.codepage = CP_ACP;
3287 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3288 ok(ret == strlen(base_string), "ret %d\n",ret);
3290 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3291 gtl.codepage = CP_ACP;
3292 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3293 ok(ret == strlen(base_string), "ret %d\n",ret);
3295 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
3297 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3298 gtl.codepage = CP_ACP;
3299 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3300 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
3302 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3303 gtl.codepage = CP_ACP;
3304 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3305 ok(ret == strlen(test_string_2), "ret %d\n",ret);
3307 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
3309 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3310 gtl.codepage = CP_ACP;
3311 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3312 ok(ret == 10, "ret %d\n",ret);
3314 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3315 gtl.codepage = CP_ACP;
3316 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3317 ok(ret == 6, "ret %d\n",ret);
3319 DestroyWindow(hwnd);
3323 /* globals that parent and child access when checking event masks & notifications */
3324 static HWND eventMaskEditHwnd = 0;
3325 static int queriedEventMask;
3326 static int watchForEventMask = 0;
3328 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
3329 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3331 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
3333 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3335 return DefWindowProcA(hwnd, message, wParam, lParam);
3338 /* test event masks in combination with WM_COMMAND */
3339 static void test_eventMask(void)
3344 const char text[] = "foo bar\n";
3347 /* register class to capture WM_COMMAND */
3349 cls.lpfnWndProc = ParentMsgCheckProcA;
3352 cls.hInstance = GetModuleHandleA(0);
3354 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3355 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3356 cls.lpszMenuName = NULL;
3357 cls.lpszClassName = "EventMaskParentClass";
3358 if(!RegisterClassA(&cls)) assert(0);
3360 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3361 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3362 ok (parent != 0, "Failed to create parent window\n");
3364 eventMaskEditHwnd = new_richedit(parent);
3365 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
3367 eventMask = ENM_CHANGE | ENM_UPDATE;
3368 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
3369 ok(ret == ENM_NONE, "wrong event mask\n");
3370 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
3371 ok(ret == eventMask, "failed to set event mask\n");
3373 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
3374 queriedEventMask = 0; /* initialize to something other than we expect */
3375 watchForEventMask = EN_CHANGE;
3376 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
3377 ok(ret == TRUE, "failed to set text\n");
3378 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
3379 notification in response to WM_SETTEXT */
3380 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
3381 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
3385 static int received_WM_NOTIFY = 0;
3386 static int modify_at_WM_NOTIFY = 0;
3387 static HWND hwndRichedit_WM_NOTIFY;
3389 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3391 if(message == WM_NOTIFY)
3393 received_WM_NOTIFY = 1;
3394 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
3396 return DefWindowProcA(hwnd, message, wParam, lParam);
3399 static void test_WM_NOTIFY(void)
3405 /* register class to capture WM_NOTIFY */
3407 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
3410 cls.hInstance = GetModuleHandleA(0);
3412 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
3413 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3414 cls.lpszMenuName = NULL;
3415 cls.lpszClassName = "WM_NOTIFY_ParentClass";
3416 if(!RegisterClassA(&cls)) assert(0);
3418 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3419 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3420 ok (parent != 0, "Failed to create parent window\n");
3422 hwndRichedit_WM_NOTIFY = new_richedit(parent);
3423 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
3425 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
3427 /* Notifications for selection change should only be sent when selection
3428 actually changes. EM_SETCHARFORMAT is one message that calls
3429 ME_CommitUndo, which should check whether message should be sent */
3430 received_WM_NOTIFY = 0;
3431 cf2.cbSize = sizeof(CHARFORMAT2);
3432 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3434 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3435 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3436 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
3437 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3439 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
3441 received_WM_NOTIFY = 0;
3442 modify_at_WM_NOTIFY = 0;
3443 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3444 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
3445 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3447 received_WM_NOTIFY = 0;
3448 modify_at_WM_NOTIFY = 0;
3449 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
3450 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3452 received_WM_NOTIFY = 0;
3453 modify_at_WM_NOTIFY = 0;
3454 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
3455 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
3456 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
3458 DestroyWindow(hwndRichedit_WM_NOTIFY);
3459 DestroyWindow(parent);
3462 START_TEST( editor )
3467 /* Must explicitly LoadLibrary(). The test has no references to functions in
3468 * RICHED20.DLL, so the linker doesn't actually link to it. */
3469 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
3470 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
3474 test_EM_SCROLLCARET();
3477 test_EM_LINELENGTH();
3478 test_EM_SETCHARFORMAT();
3479 test_EM_SETTEXTMODE();
3480 test_TM_PLAINTEXT();
3481 test_EM_SETOPTIONS();
3483 test_EM_GETTEXTRANGE();
3484 test_EM_GETSELTEXT();
3485 test_EM_SETUNDOLIMIT();
3487 test_EM_SETTEXTEX();
3488 test_EM_LIMITTEXT();
3489 test_EM_EXLIMITTEXT();
3490 test_EM_GETLIMITTEXT();
3492 test_EM_GETMODIFY();
3495 test_EM_AUTOURLDETECT();
3497 test_EM_STREAMOUT();
3498 test_EM_StreamIn_Undo();
3499 test_EM_FORMATRANGE();
3500 test_unicode_conversions();
3501 test_EM_GETTEXTLENGTHEX();
3502 test_EM_REPLACESEL(1);
3503 test_EM_REPLACESEL(0);
3507 /* Set the environment variable WINETEST_RICHED20 to keep windows
3508 * responsive and open for 30 seconds. This is useful for debugging.
3510 * The message pump uses PeekMessage() to empty the queue and then sleeps for
3511 * 50ms before retrying the queue. */
3512 end = time(NULL) + 30;
3513 if (getenv( "WINETEST_RICHED20" )) {
3514 while (time(NULL) < end) {
3515 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
3516 TranslateMessage(&msg);
3517 DispatchMessage(&msg);
3524 OleFlushClipboard();
3525 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());