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
32 #include <wine/test.h>
34 static HMODULE hmoduleRichEdit;
36 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
38 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
39 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
40 hmoduleRichEdit, NULL);
41 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
45 static HWND new_richedit(HWND parent) {
46 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
49 static const char haystack[] = "WINEWine wineWine wine WineWine";
62 struct find_s find_tests[] = {
63 /* Find in empty text */
64 {0, -1, "foo", FR_DOWN, -1, 0},
65 {0, -1, "foo", 0, -1, 0},
66 {0, -1, "", FR_DOWN, -1, 0},
67 {20, 5, "foo", FR_DOWN, -1, 0},
68 {5, 20, "foo", FR_DOWN, -1, 0}
71 struct find_s find_tests2[] = {
73 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
74 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
76 /* Subsequent finds */
77 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
78 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
79 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
80 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
83 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
84 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
85 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
87 /* Case-insensitive */
88 {1, 31, "wInE", FR_DOWN, 4, 0},
89 {1, 31, "Wine", FR_DOWN, 4, 0},
91 /* High-to-low ranges */
92 {20, 5, "Wine", FR_DOWN, -1, 0},
93 {2, 1, "Wine", FR_DOWN, -1, 0},
94 {30, 29, "Wine", FR_DOWN, -1, 0},
95 {20, 5, "Wine", 0, 13, 0},
98 {5, 10, "", FR_DOWN, -1, 0},
99 {10, 5, "", FR_DOWN, -1, 0},
100 {0, -1, "", FR_DOWN, -1, 0},
101 {10, 5, "", 0, -1, 0},
103 /* Whole-word search */
104 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
105 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
106 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
107 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
108 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
109 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
110 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
113 {5, 200, "XXX", FR_DOWN, -1, 0},
114 {-20, 20, "Wine", FR_DOWN, -1, 0},
115 {-20, 20, "Wine", FR_DOWN, -1, 0},
116 {-15, -20, "Wine", FR_DOWN, -1, 0},
117 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
119 /* Check the case noted in bug 4479 where matches at end aren't recognized */
120 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
121 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
122 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
123 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
124 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
126 /* The backwards case of bug 4479; bounds look right
127 * Fails because backward find is wrong */
128 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
129 {0, 20, "WINE", FR_MATCHCASE, -1, 0}
132 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
135 memset(&ft, 0, sizeof(ft));
136 ft.chrg.cpMin = f->start;
137 ft.chrg.cpMax = f->end;
138 ft.lpstrText = f->needle;
139 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
140 ok(findloc == f->expected_loc,
141 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
142 name, id, f->needle, f->start, f->end, f->flags, findloc);
145 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
149 memset(&ft, 0, sizeof(ft));
150 ft.chrg.cpMin = f->start;
151 ft.chrg.cpMax = f->end;
152 ft.lpstrText = f->needle;
153 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
154 ok(findloc == f->expected_loc,
155 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
156 name, id, f->needle, f->start, f->end, f->flags, findloc);
157 ok(ft.chrgText.cpMin == f->expected_loc,
158 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
159 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
160 ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
161 : f->expected_loc + strlen(f->needle)),
162 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n",
163 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
166 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
171 for (i = 0; i < num_tests; i++) {
172 if (find[i]._todo_wine) {
174 check_EM_FINDTEXT(hwnd, name, &find[i], i);
175 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
178 check_EM_FINDTEXT(hwnd, name, &find[i], i);
179 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
184 static void test_EM_FINDTEXT(void)
186 HWND hwndRichEdit = new_richedit(NULL);
188 /* Empty rich edit control */
189 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
190 sizeof(find_tests)/sizeof(struct find_s));
192 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
195 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
196 sizeof(find_tests2)/sizeof(struct find_s));
198 DestroyWindow(hwndRichEdit);
201 static const struct getline_s {
206 {0, 10, "foo bar\r"},
211 /* Buffer smaller than line length */
217 static void test_EM_GETLINE(void)
220 HWND hwndRichEdit = new_richedit(NULL);
221 static const int nBuf = 1024;
222 char dest[1024], origdest[1024];
223 const char text[] = "foo bar\n"
227 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
229 memset(origdest, 0xBB, nBuf);
230 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
233 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
234 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
235 memset(dest, 0xBB, nBuf);
236 *(WORD *) dest = gl[i].buffer_len;
238 /* EM_GETLINE appends a "\r\0" to the end of the line
239 * nCopied counts up to and including the '\r' */
240 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
241 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
243 /* two special cases since a parameter is passed via dest */
244 if (gl[i].buffer_len == 0)
245 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
247 else if (gl[i].buffer_len == 1)
248 ok(dest[0] == gl[i].text[0] && !dest[1] &&
249 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
252 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
253 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
254 ok(!strncmp(dest + expected_bytes_written, origdest
255 + expected_bytes_written, nBuf - expected_bytes_written),
256 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
260 DestroyWindow(hwndRichEdit);
263 static int get_scroll_pos_y(HWND hwnd)
266 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
267 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
271 static void move_cursor(HWND hwnd, long charindex)
274 cr.cpMax = charindex;
275 cr.cpMin = charindex;
276 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
279 static void line_scroll(HWND hwnd, int amount)
281 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
284 static void test_EM_SCROLLCARET(void)
287 HWND hwndRichEdit = new_richedit(NULL);
288 const char text[] = "aa\n"
289 "this is a long line of text that should be longer than the "
298 /* Can't verify this */
299 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
301 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
303 /* Caret above visible window */
304 line_scroll(hwndRichEdit, 3);
305 prevY = get_scroll_pos_y(hwndRichEdit);
306 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
307 curY = get_scroll_pos_y(hwndRichEdit);
308 ok(prevY != curY, "%d == %d\n", prevY, curY);
310 /* Caret below visible window */
311 move_cursor(hwndRichEdit, sizeof(text) - 1);
312 line_scroll(hwndRichEdit, -3);
313 prevY = get_scroll_pos_y(hwndRichEdit);
314 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
315 curY = get_scroll_pos_y(hwndRichEdit);
316 ok(prevY != curY, "%d == %d\n", prevY, curY);
318 /* Caret in visible window */
319 move_cursor(hwndRichEdit, sizeof(text) - 2);
320 prevY = get_scroll_pos_y(hwndRichEdit);
321 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
322 curY = get_scroll_pos_y(hwndRichEdit);
323 ok(prevY == curY, "%d != %d\n", prevY, curY);
325 /* Caret still in visible window */
326 line_scroll(hwndRichEdit, -1);
327 prevY = get_scroll_pos_y(hwndRichEdit);
328 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
329 curY = get_scroll_pos_y(hwndRichEdit);
330 ok(prevY == curY, "%d != %d\n", prevY, curY);
332 DestroyWindow(hwndRichEdit);
335 static void test_EM_SETTEXTMODE(void)
337 HWND hwndRichEdit = new_richedit(NULL);
338 CHARFORMAT2 cf2, cf2test;
342 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
343 /*Insert text into the control*/
345 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
347 /*Attempt to change the control to plain text mode*/
348 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
349 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
351 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
352 If rich text is pasted, it should have the same formatting as the rest
353 of the text in the control*/
356 *NOTE: If the default text was already italicized, the test will simply
357 reverse; in other words, it will copy a regular "wine" into a plain
358 text window that uses an italicized format*/
359 cf2.cbSize = sizeof(CHARFORMAT2);
360 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
363 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
364 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
366 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
367 however, SCF_ALL has been implemented*/
368 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
369 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
371 /*Select the string "wine"*/
374 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
376 /*Copy the italicized "wine" to the clipboard*/
377 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
379 /*Reset the formatting to default*/
380 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
381 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
383 /*Clear the text in the control*/
384 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
386 /*Switch to Plain Text Mode*/
387 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
388 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
390 /*Input "wine" again in normal format*/
391 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
393 /*Paste the italicized "wine" into the control*/
394 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
396 /*Select a character from the first "wine" string*/
399 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
401 /*Retrieve its formatting*/
402 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
405 /*Select a character from the second "wine" string*/
408 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
410 /*Retrieve its formatting*/
411 cf2test.cbSize = sizeof(CHARFORMAT2);
412 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
415 /*Compare the two formattings*/
416 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
417 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
418 cf2.dwEffects, cf2test.dwEffects);
419 /*Test TM_RICHTEXT by: switching back to Rich Text mode
420 printing "wine" in the current format(normal)
421 pasting "wine" from the clipboard(italicized)
422 comparing the two formats(should differ)*/
424 /*Attempt to switch with text in control*/
425 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
426 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
431 /*Switch into Rich Text mode*/
432 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
433 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
435 /*Print "wine" in normal formatting into the control*/
436 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
438 /*Paste italicized "wine" into the control*/
439 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
441 /*Select text from the first "wine" string*/
444 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
446 /*Retrieve its formatting*/
447 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
450 /*Select text from the second "wine" string*/
453 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
455 /*Retrieve its formatting*/
456 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
459 /*Test that the two formattings are not the same*/
460 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
461 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
462 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
464 DestroyWindow(hwndRichEdit);
467 static void test_TM_PLAINTEXT(void)
469 /*Tests plain text properties*/
471 HWND hwndRichEdit = new_richedit(NULL);
472 CHARFORMAT2 cf2, cf2test;
475 /*Switch to plain text mode*/
477 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
478 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
480 /*Fill control with text*/
482 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
484 /*Select some text and bold it*/
488 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
489 cf2.cbSize = sizeof(CHARFORMAT2);
490 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
493 cf2.dwMask = CFM_BOLD | cf2.dwMask;
494 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
496 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
498 /*Get the formatting of those characters*/
500 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
502 /*Get the formatting of some other characters*/
503 cf2test.cbSize = sizeof(CHARFORMAT2);
506 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
507 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
509 /*Test that they are the same as plain text allows only one formatting*/
511 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
512 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
513 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
515 /*Fill the control with a "wine" string, which when inserted will be bold*/
517 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
519 /*Copy the bolded "wine" string*/
523 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
524 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
526 /*Swap back to rich text*/
528 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
529 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
531 /*Set the default formatting to bold italics*/
533 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
534 cf2.dwMask |= CFM_ITALIC;
535 cf2.dwEffects ^= CFE_ITALIC;
536 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
538 /*Set the text in the control to "wine", which will be bold and italicized*/
540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
542 /*Paste the plain text "wine" string, which should take the insert
543 formatting, which at the moment is bold italics*/
545 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
547 /*Select the first "wine" string and retrieve its formatting*/
551 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
552 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
554 /*Select the second "wine" string and retrieve its formatting*/
558 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
559 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
561 /*Compare the two formattings. They should be the same.*/
563 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
564 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
565 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
566 DestroyWindow(hwndRichEdit);
569 static void test_WM_GETTEXT(void)
571 HWND hwndRichEdit = new_richedit(NULL);
572 static const char text[] = "Hello. My name is RichEdit!";
573 char buffer[1024] = {0};
576 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
577 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
578 result = strcmp(buffer,text);
580 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
581 DestroyWindow(hwndRichEdit);
584 /* FIXME: need to test unimplemented options and robustly test wparam */
585 static void test_EM_SETOPTIONS(void)
587 HWND hwndRichEdit = new_richedit(NULL);
588 static const char text[] = "Hello. My name is RichEdit!";
589 char buffer[1024] = {0};
591 /* NEGATIVE TESTING - NO OPTIONS SET */
592 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
593 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
595 /* testing no readonly by sending 'a' to the control*/
596 SetFocus(hwndRichEdit);
597 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
598 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
600 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
601 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
603 /* READONLY - sending 'a' to the control */
604 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
605 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
606 SetFocus(hwndRichEdit);
607 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
608 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
609 ok(buffer[0]==text[0],
610 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
612 DestroyWindow(hwndRichEdit);
615 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
617 CHARFORMAT2W text_format;
618 int link_present = 0;
619 text_format.cbSize = sizeof(text_format);
620 SendMessage(hwnd, EM_SETSEL, 0, 0);
621 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
622 link_present = text_format.dwEffects & CFE_LINK;
624 { /* control text is url; should get CFE_LINK */
625 ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
629 ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
633 static HWND new_static_wnd(HWND parent) {
634 return new_window("Static", 0, parent);
637 static void test_EM_AUTOURLDETECT(void)
644 {"http://www.winehq.org", 1},
645 {"http//winehq.org", 0},
646 {"ww.winehq.org", 0},
647 {"www.winehq.org", 1},
648 {"ftp://192.168.1.1", 1},
649 {"ftp//192.168.1.1", 0},
650 {"mailto:your@email.com", 1},
651 {"prospero:prosperoserver", 1},
653 {"news:newserver", 1},
654 {"wais:waisserver", 1}
659 HWND hwndRichEdit, parent;
661 parent = new_static_wnd(NULL);
662 hwndRichEdit = new_richedit(parent);
663 /* Try and pass EM_AUTOURLDETECT some test wParam values */
664 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
665 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
666 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
667 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
668 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
669 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
670 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
671 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
672 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
673 /* for each url, check the text to see if CFE_LINK effect is present */
674 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
675 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
676 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
677 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
678 check_CFE_LINK_rcvd(hwndRichEdit, 0);
679 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
680 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
681 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
682 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
684 DestroyWindow(hwndRichEdit);
685 DestroyWindow(parent);
688 static void test_EM_SCROLL(void)
691 int r; /* return value */
692 int expr; /* expected return value */
693 HWND hwndRichEdit = new_richedit(NULL);
694 int y_before, y_after; /* units of lines of text */
696 /* test a richedit box containing a single line of text */
697 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
699 for (i = 0; i < 4; i++) {
700 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
702 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
703 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
704 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
705 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
706 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
707 "(i == %d)\n", y_after, i);
711 * test a richedit box that will scroll. There are two general
712 * cases: the case without any long lines and the case with a long
715 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
717 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
719 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
720 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
721 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
722 "LONG LINE \nb\nc\nd\ne");
723 for (j = 0; j < 12; j++) /* reset scrol position to top */
724 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
726 /* get first visible line */
727 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
728 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
730 /* get new current first visible line */
731 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
733 ok(((r & 0xffffff00) == 0x00010000) &&
734 ((r & 0x000000ff) != 0x00000000),
735 "EM_SCROLL page down didn't scroll by a small positive number of "
736 "lines (r == 0x%08x)\n", r);
737 ok(y_after > y_before, "EM_SCROLL page down not functioning "
738 "(line %d scrolled to line %d\n", y_before, y_after);
742 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
743 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
744 ok(((r & 0xffffff00) == 0x0001ff00),
745 "EM_SCROLL page up didn't scroll by a small negative number of lines "
746 "(r == 0x%08x)\n", r);
747 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
748 "%d scrolled to line %d\n", y_before, y_after);
752 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
754 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
756 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
757 "(r == 0x%08x)\n", r);
758 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
759 "1 line (%d scrolled to %d)\n", y_before, y_after);
763 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
765 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
767 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
768 "(r == 0x%08x)\n", r);
769 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
770 "line (%d scrolled to %d)\n", y_before, y_after);
774 r = SendMessage(hwndRichEdit, EM_SCROLL,
775 SB_LINEUP, 0); /* lineup beyond top */
777 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
780 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
781 ok(y_before == y_after,
782 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
786 r = SendMessage(hwndRichEdit, EM_SCROLL,
787 SB_PAGEUP, 0);/*page up beyond top */
789 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
792 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
793 ok(y_before == y_after,
794 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
796 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
797 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
798 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
799 r = SendMessage(hwndRichEdit, EM_SCROLL,
800 SB_PAGEDOWN, 0); /* page down beyond bot */
801 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
804 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
805 ok(y_before == y_after,
806 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
809 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
810 SendMessage(hwndRichEdit, EM_SCROLL,
811 SB_LINEDOWN, 0); /* line down beyond bot */
812 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
815 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
816 ok(y_before == y_after,
817 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
820 DestroyWindow(hwndRichEdit);
823 static void test_EM_SETUNDOLIMIT(void)
825 /* cases we test for:
826 * default behaviour - limiting at 100 undo's
827 * undo disabled - setting a limit of 0
828 * undo limited - undo limit set to some to some number, like 2
829 * bad input - sending a negative number should default to 100 undo's */
831 HWND hwndRichEdit = new_richedit(NULL);
836 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
839 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
840 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
841 also, multiple pastes don't combine like WM_CHAR would */
842 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
844 /* first case - check the default */
845 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
846 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
847 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
848 for (i=0; i<100; i++) /* Undo 100 of them */
849 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
850 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
851 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
853 /* second case - cannot undo */
854 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
855 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
856 SendMessage(hwndRichEdit,
857 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
858 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
859 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
861 /* third case - set it to an arbitrary number */
862 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
863 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
864 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
865 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
866 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
867 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
868 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
869 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
870 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
871 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
872 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
873 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
874 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
875 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
877 /* fourth case - setting negative numbers should default to 100 undos */
878 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
879 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
881 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
883 DestroyWindow(hwndRichEdit);
886 static void test_ES_PASSWORD(void)
888 /* This isn't hugely testable, so we're just going to run it through it's paces. */
890 HWND hwndRichEdit = new_richedit(NULL);
893 /* First, check the default of a regular control */
894 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
896 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
898 /* Now, set it to something normal */
899 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
900 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
902 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
904 /* Now, set it to something odd */
905 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
906 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
908 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
909 DestroyWindow(hwndRichEdit);
912 static void test_EM_SETTEXTEX(void)
914 HWND hwndRichEdit = new_richedit(NULL);
917 WCHAR TestItem1[] = {'T', 'e', 's', 't',
919 'T', 'e', 'x', 't', 0};
920 #define MAX_BUF_LEN 1024
921 WCHAR buf[MAX_BUF_LEN];
925 setText.codepage = 1200; /* no constant for unicode */
926 getText.codepage = 1200; /* no constant for unicode */
927 getText.cb = MAX_BUF_LEN;
928 getText.flags = GT_DEFAULT;
931 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
932 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
933 ok(lstrcmpW(buf, TestItem1) == 0,
934 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
936 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
937 (WPARAM)&setText, (LPARAM) NULL);
938 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
941 "EM_SETTEXTEX returned %d, instead of 1\n",result);
942 ok(lstrlenW(buf) == 0,
943 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
945 /* put some text back */
947 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
948 /* select some text */
951 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
952 /* replace current selection */
953 setText.flags = ST_SELECTION;
954 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
955 (WPARAM)&setText, (LPARAM) NULL);
957 "EM_SETTEXTEX with NULL lParam to replace selection"
958 " with no text should return 0. Got %i\n",
961 /* put some text back */
963 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
964 /* select some text */
967 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
968 /* replace current selection */
969 setText.flags = ST_SELECTION;
970 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
971 (WPARAM)&setText, (LPARAM) TestItem1);
973 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
974 ok(result == lstrlenW(TestItem1),
975 "EM_SETTEXTEX with NULL lParam to replace selection"
976 " with no text should return 0. Got %i\n",
978 ok(lstrlenW(buf) == 22,
979 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
982 DestroyWindow(hwndRichEdit);
985 static void test_EM_LIMITTEXT(void)
989 HWND hwndRichEdit = new_richedit(NULL);
991 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
992 * about setting the length to -1 for multiline edit controls doesn't happen.
995 /* Don't check default gettextlimit case. That's done in other tests */
997 /* Set textlimit to 100 */
998 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
999 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1001 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1003 /* Set textlimit to 0 */
1004 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1005 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1007 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1009 /* Set textlimit to -1 */
1010 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1011 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1013 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1015 /* Set textlimit to -2 */
1016 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1017 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1019 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1021 DestroyWindow (hwndRichEdit);
1025 static void test_EM_EXLIMITTEXT(void)
1027 int i, selBegin, selEnd, len1, len2;
1029 char text[1024 + 1];
1030 char buffer[1024 + 1];
1031 int textlimit = 0; /* multiple of 100 */
1032 HWND hwndRichEdit = new_richedit(NULL);
1034 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1035 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1038 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1039 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1041 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1044 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1045 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1047 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1049 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1050 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1051 /* default for WParam = 0 */
1052 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1054 textlimit = sizeof(text)-1;
1055 memset(text, 'W', textlimit);
1056 text[sizeof(text)-1] = 0;
1057 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1058 /* maxed out text */
1059 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1061 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1062 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1063 len1 = selEnd - selBegin;
1065 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1066 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1067 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1068 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1069 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1070 len2 = selEnd - selBegin;
1073 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1076 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1077 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1078 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1079 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1080 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1081 len1 = selEnd - selBegin;
1084 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1087 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1088 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1089 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1090 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1091 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1092 len2 = selEnd - selBegin;
1095 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1098 /* set text up to the limit, select all the text, then add a char */
1100 memset(text, 'W', textlimit);
1101 text[textlimit] = 0;
1102 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1103 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1104 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1105 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1106 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1107 result = strcmp(buffer, "A");
1108 ok(0 == result, "got string = \"%s\"\n", buffer);
1110 DestroyWindow(hwndRichEdit);
1113 static void test_EM_GETLIMITTEXT(void)
1116 HWND hwndRichEdit = new_richedit(NULL);
1118 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1119 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1121 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1122 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1123 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1125 DestroyWindow(hwndRichEdit);
1128 static void test_WM_SETFONT(void)
1130 /* There is no invalid input or error conditions for this function.
1131 * NULL wParam and lParam just fall back to their default values
1132 * It should be noted that even if you use a gibberish name for your fonts
1133 * here, it will still work because the name is stored. They will display as
1134 * System, but will report their name to be whatever they were created as */
1136 HWND hwndRichEdit = new_richedit(NULL);
1137 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1138 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1139 FF_DONTCARE, "Marlett");
1140 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1141 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1142 FF_DONTCARE, "MS Sans Serif");
1143 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1144 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1145 FF_DONTCARE, "Courier");
1146 LOGFONTA sentLogFont;
1147 CHARFORMAT2A returnedCF2A;
1149 returnedCF2A.cbSize = sizeof(returnedCF2A);
1151 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1152 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1153 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1155 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1156 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1157 "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1158 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1160 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1161 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1162 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1163 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1164 "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1165 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1167 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1168 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1169 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1170 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1171 "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1172 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1174 /* This last test is special since we send in NULL. We clear the variables
1175 * and just compare to "System" instead of the sent in font name. */
1176 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1177 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1178 returnedCF2A.cbSize = sizeof(returnedCF2A);
1180 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1181 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1182 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1183 ok (!strcmp("System",returnedCF2A.szFaceName),
1184 "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1186 DestroyWindow(hwndRichEdit);
1190 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1195 const char** str = (const char**)dwCookie;
1196 int size = strlen(*str);
1197 if(size > 3) /* let's make it peice-meal for fun */
1204 memcpy(pbBuff, *str, *pcb);
1210 static void test_EM_GETMODIFY(void)
1212 HWND hwndRichEdit = new_richedit(NULL);
1215 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1217 'T', 'e', 'x', 't', 0};
1218 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1220 'O', 't', 'h', 'e', 'r',
1221 'T', 'e', 'x', 't', 0};
1222 const char* streamText = "hello world";
1227 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1228 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1229 FF_DONTCARE, "Courier");
1231 setText.codepage = 1200; /* no constant for unicode */
1232 setText.flags = ST_KEEPUNDO;
1235 /* modify flag shouldn't be set when richedit is first created */
1236 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1238 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1240 /* setting modify flag should actually set it */
1241 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1242 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1244 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1246 /* clearing modify flag should actually clear it */
1247 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1248 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1250 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1252 /* setting font doesn't change modify flag */
1253 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1254 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1255 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1257 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1259 /* setting text should set modify flag */
1260 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1261 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1262 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1264 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1266 /* undo previous text doesn't reset modify flag */
1267 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1268 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1270 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1272 /* set text with no flag to keep undo stack should not set modify flag */
1273 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1275 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1276 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1278 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1280 /* WM_SETTEXT doesn't modify */
1281 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1282 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1283 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1286 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1289 /* clear the text */
1290 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1291 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1292 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1294 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1297 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1298 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1299 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1300 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1301 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1303 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1305 /* copy/paste text 1 */
1306 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1307 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1308 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1309 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1310 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1312 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1314 /* copy/paste text 2 */
1315 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1316 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1317 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1318 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1319 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1320 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1322 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1325 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1326 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1327 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1328 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1330 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1333 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1334 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1335 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1336 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1338 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1340 /* set char format */
1341 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1342 cf2.cbSize = sizeof(CHARFORMAT2);
1343 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1345 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1346 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1347 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1348 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1350 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1352 /* set para format */
1353 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1354 pf2.cbSize = sizeof(PARAFORMAT2);
1355 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1357 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1358 pf2.wAlignment = PFA_RIGHT;
1359 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1360 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1362 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1365 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1366 es.dwCookie = (DWORD_PTR)&streamText;
1368 es.pfnCallback = test_EM_GETMODIFY_esCallback;
1369 SendMessage(hwndRichEdit, EM_STREAMIN,
1370 (WPARAM)(SF_TEXT), (LPARAM)&es);
1371 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1373 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1375 DestroyWindow(hwndRichEdit);
1381 long expected_retval;
1382 int expected_getsel_start;
1383 int expected_getsel_end;
1384 int _exsetsel_todo_wine;
1385 int _getsel_todo_wine;
1388 const struct exsetsel_s exsetsel_tests[] = {
1390 {5, 10, 10, 5, 10, 0, 0},
1391 {15, 17, 17, 15, 17, 0, 0},
1392 /* test cpMax > strlen() */
1393 {0, 100, 18, 0, 18, 0, 1},
1394 /* test cpMin == cpMax */
1395 {5, 5, 5, 5, 5, 0, 0},
1396 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1397 {-1, 0, 5, 5, 5, 0, 0},
1398 {-1, 17, 5, 5, 5, 0, 0},
1399 {-1, 18, 5, 5, 5, 0, 0},
1400 /* test cpMin < 0 && cpMax < 0 */
1401 {-1, -1, 17, 17, 17, 0, 0},
1402 {-4, -5, 17, 17, 17, 0, 0},
1403 /* test cMin >=0 && cpMax < 0 (bug 6814) */
1404 {0, -1, 18, 0, 18, 0, 1},
1405 {17, -5, 18, 17, 18, 0, 1},
1406 {18, -3, 17, 17, 17, 0, 0},
1407 /* test if cpMin > cpMax */
1408 {15, 19, 18, 15, 18, 0, 1},
1409 {19, 15, 18, 15, 18, 0, 1}
1412 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1417 cr.cpMin = setsel->min;
1418 cr.cpMax = setsel->max;
1419 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1421 if (setsel->_exsetsel_todo_wine) {
1423 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1426 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1429 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1431 if (setsel->_getsel_todo_wine) {
1433 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);
1436 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);
1440 static void test_EM_EXSETSEL(void)
1442 HWND hwndRichEdit = new_richedit(NULL);
1444 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1446 /* sending some text to the window */
1447 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1448 /* 01234567890123456*/
1451 for (i = 0; i < num_tests; i++) {
1452 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1455 DestroyWindow(hwndRichEdit);
1458 static void test_WM_PASTE(void)
1461 char buffer[1024] = {0};
1462 const char* text1 = "testing paste\r";
1463 const char* text2 = "testing paste\r\rtesting paste";
1464 const char* text3 = "testing paste\rpaste\rtesting paste";
1465 HWND hwndRichEdit = new_richedit(NULL);
1467 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1468 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1469 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1470 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1471 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1472 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1473 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1474 result = strcmp(text1, buffer);
1476 "test paste: strcmp = %i\n", result);
1478 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1479 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1480 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1481 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1482 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1483 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1484 SendMessage(hwndRichEdit, WM_CHAR, 25, 0); /* ctrl-y */
1485 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1486 result = strcmp(buffer,text3);
1488 "test paste: strcmp = %i\n", result);
1490 DestroyWindow(hwndRichEdit);
1493 static int nCallbackCount = 0;
1495 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1498 const char text[] = {'t','e','s','t'};
1500 if (sizeof(text) <= cb)
1502 if ((int)dwCookie != nCallbackCount)
1508 memcpy (pbBuff, text, sizeof(text));
1509 *pcb = sizeof(text);
1516 return 1; /* indicates callback failed */
1519 static void test_EM_StreamIn_Undo(void)
1521 /* The purpose of this test is to determine when a EM_StreamIn should be
1522 * undoable. This is important because WM_PASTE currently uses StreamIn and
1523 * pasting should always be undoable but streaming isn't always.
1526 * StreamIn plain text without SFF_SELECTION.
1527 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1528 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1529 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1530 * Feel free to add tests for other text modes or StreamIn things.
1534 HWND hwndRichEdit = new_richedit(NULL);
1537 char buffer[1024] = {0};
1538 const char randomtext[] = "Some text";
1540 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1542 /* StreamIn, no SFF_SELECTION */
1543 es.dwCookie = nCallbackCount;
1544 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1545 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1546 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1547 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1548 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1549 result = strcmp (buffer,"test");
1551 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1553 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1554 ok (result == FALSE,
1555 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1557 /* StreamIn, SFF_SELECTION, but nothing selected */
1558 es.dwCookie = nCallbackCount;
1559 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1560 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1561 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1562 SendMessage(hwndRichEdit, EM_STREAMIN,
1563 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1564 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1565 result = strcmp (buffer,"testSome text");
1567 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1569 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1571 "EM_STREAMIN with SFF_SELECTION but no selection set "
1572 "should create an undo\n");
1574 /* StreamIn, SFF_SELECTION, with a selection */
1575 es.dwCookie = nCallbackCount;
1576 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1577 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1578 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1579 SendMessage(hwndRichEdit, EM_STREAMIN,
1580 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1581 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1582 result = strcmp (buffer,"Sometesttext");
1584 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1586 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1588 "EM_STREAMIN with SFF_SELECTION and selection set "
1589 "should create an undo\n");
1593 static BOOL is_em_settextex_supported(HWND hwnd)
1595 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
1596 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
1599 static void test_unicode_conversions(void)
1601 static const WCHAR tW[] = {'t',0};
1602 static const WCHAR teW[] = {'t','e',0};
1603 static const WCHAR textW[] = {'t','e','s','t',0};
1604 static const char textA[] = "test";
1608 int is_win9x, em_settextex_supported, ret;
1610 is_win9x = GetVersion() & 0x80000000;
1612 #define set_textA(hwnd, wm_set_text, txt) \
1614 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1615 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1616 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1617 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1618 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1620 #define expect_textA(hwnd, wm_get_text, txt) \
1622 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1623 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
1624 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1625 memset(bufA, 0xAA, sizeof(bufA)); \
1626 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1627 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1628 ret = lstrcmpA(bufA, txt); \
1629 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1632 #define set_textW(hwnd, wm_set_text, txt) \
1634 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1635 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1636 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1637 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1638 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1640 #define expect_textW(hwnd, wm_get_text, txt) \
1642 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1643 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
1644 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1645 memset(bufW, 0xAA, sizeof(bufW)); \
1648 assert(wm_get_text == EM_GETTEXTEX); \
1649 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1650 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1654 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1655 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1657 ret = lstrcmpW(bufW, txt); \
1658 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1660 #define expect_empty(hwnd, wm_get_text) \
1662 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1663 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
1664 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1665 memset(bufA, 0xAA, sizeof(bufA)); \
1666 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1667 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1668 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1671 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1672 0, 0, 200, 60, 0, 0, 0, 0);
1673 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1675 ret = IsWindowUnicode(hwnd);
1677 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1679 ok(ret, "RichEdit20W should be unicode under NT\n");
1681 /* EM_SETTEXTEX is supported starting from version 3.0 */
1682 em_settextex_supported = is_em_settextex_supported(hwnd);
1683 trace("EM_SETTEXTEX is %ssupported on this platform\n",
1684 em_settextex_supported ? "" : "NOT ");
1686 expect_empty(hwnd, WM_GETTEXT);
1687 expect_empty(hwnd, EM_GETTEXTEX);
1689 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1690 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1691 expect_textA(hwnd, WM_GETTEXT, "t");
1692 expect_textA(hwnd, EM_GETTEXTEX, "t");
1693 expect_textW(hwnd, EM_GETTEXTEX, tW);
1695 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1696 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1697 expect_textA(hwnd, WM_GETTEXT, "te");
1698 expect_textA(hwnd, EM_GETTEXTEX, "te");
1699 expect_textW(hwnd, EM_GETTEXTEX, teW);
1701 set_textA(hwnd, WM_SETTEXT, NULL);
1702 expect_empty(hwnd, WM_GETTEXT);
1703 expect_empty(hwnd, EM_GETTEXTEX);
1706 set_textA(hwnd, WM_SETTEXT, textW);
1708 set_textA(hwnd, WM_SETTEXT, textA);
1709 expect_textA(hwnd, WM_GETTEXT, textA);
1710 expect_textA(hwnd, EM_GETTEXTEX, textA);
1711 expect_textW(hwnd, EM_GETTEXTEX, textW);
1713 if (em_settextex_supported)
1715 set_textA(hwnd, EM_SETTEXTEX, textA);
1716 expect_textA(hwnd, WM_GETTEXT, textA);
1717 expect_textA(hwnd, EM_GETTEXTEX, textA);
1718 expect_textW(hwnd, EM_GETTEXTEX, textW);
1723 set_textW(hwnd, WM_SETTEXT, textW);
1724 expect_textW(hwnd, WM_GETTEXT, textW);
1725 expect_textA(hwnd, WM_GETTEXT, textA);
1726 expect_textW(hwnd, EM_GETTEXTEX, textW);
1727 expect_textA(hwnd, EM_GETTEXTEX, textA);
1729 if (em_settextex_supported)
1731 set_textW(hwnd, EM_SETTEXTEX, textW);
1732 expect_textW(hwnd, WM_GETTEXT, textW);
1733 expect_textA(hwnd, WM_GETTEXT, textA);
1734 expect_textW(hwnd, EM_GETTEXTEX, textW);
1735 expect_textA(hwnd, EM_GETTEXTEX, textA);
1738 DestroyWindow(hwnd);
1740 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
1741 0, 0, 200, 60, 0, 0, 0, 0);
1742 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1744 ret = IsWindowUnicode(hwnd);
1745 ok(!ret, "RichEdit20A should NOT be unicode\n");
1747 set_textA(hwnd, WM_SETTEXT, textA);
1748 expect_textA(hwnd, WM_GETTEXT, textA);
1749 expect_textA(hwnd, EM_GETTEXTEX, textA);
1750 expect_textW(hwnd, EM_GETTEXTEX, textW);
1752 if (em_settextex_supported)
1754 set_textA(hwnd, EM_SETTEXTEX, textA);
1755 expect_textA(hwnd, WM_GETTEXT, textA);
1756 expect_textA(hwnd, EM_GETTEXTEX, textA);
1757 expect_textW(hwnd, EM_GETTEXTEX, textW);
1762 set_textW(hwnd, WM_SETTEXT, textW);
1763 expect_textW(hwnd, WM_GETTEXT, textW);
1764 expect_textA(hwnd, WM_GETTEXT, textA);
1765 expect_textW(hwnd, EM_GETTEXTEX, textW);
1766 expect_textA(hwnd, EM_GETTEXTEX, textA);
1768 if (em_settextex_supported)
1770 set_textW(hwnd, EM_SETTEXTEX, textW);
1771 expect_textW(hwnd, WM_GETTEXT, textW);
1772 expect_textA(hwnd, WM_GETTEXT, textA);
1773 expect_textW(hwnd, EM_GETTEXTEX, textW);
1774 expect_textA(hwnd, EM_GETTEXTEX, textA);
1777 DestroyWindow(hwnd);
1780 START_TEST( editor )
1785 /* Must explicitly LoadLibrary(). The test has no references to functions in
1786 * RICHED20.DLL, so the linker doesn't actually link to it. */
1787 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
1788 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1792 test_EM_SCROLLCARET();
1794 test_EM_SETTEXTMODE();
1795 test_TM_PLAINTEXT();
1796 test_EM_SETOPTIONS();
1798 test_EM_AUTOURLDETECT();
1799 test_EM_SETUNDOLIMIT();
1801 test_EM_SETTEXTEX();
1802 test_EM_LIMITTEXT();
1803 test_EM_EXLIMITTEXT();
1804 test_EM_GETLIMITTEXT();
1806 test_EM_GETMODIFY();
1809 test_EM_StreamIn_Undo();
1810 test_unicode_conversions();
1812 /* Set the environment variable WINETEST_RICHED20 to keep windows
1813 * responsive and open for 30 seconds. This is useful for debugging.
1815 * The message pump uses PeekMessage() to empty the queue and then sleeps for
1816 * 50ms before retrying the queue. */
1817 end = time(NULL) + 30;
1818 if (getenv( "WINETEST_RICHED20" )) {
1819 while (time(NULL) < end) {
1820 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1821 TranslateMessage(&msg);
1822 DispatchMessage(&msg);
1829 OleFlushClipboard();
1830 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());