2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <wine/test.h>
26 static HMODULE hmoduleRichEdit;
28 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
30 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
31 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
32 hmoduleRichEdit, NULL);
33 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
37 static HWND new_richedit(HWND parent) {
38 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
41 static const char haystack[] = "WINEWine wineWine wine WineWine";
53 struct find_s find_tests[] = {
54 /* Find in empty text */
55 {0, -1, "foo", FR_DOWN, -1, 0},
56 {0, -1, "foo", 0, -1, 0},
57 {0, -1, "", FR_DOWN, -1, 0},
58 {20, 5, "foo", FR_DOWN, -1, 0},
59 {5, 20, "foo", FR_DOWN, -1, 0}
62 struct find_s find_tests2[] = {
64 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
65 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
67 /* Subsequent finds */
68 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
69 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
70 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
71 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
74 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
75 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
76 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
78 /* Case-insensitive */
79 {1, 31, "wInE", FR_DOWN, 4, 0},
80 {1, 31, "Wine", FR_DOWN, 4, 0},
82 /* High-to-low ranges */
83 {20, 5, "Wine", FR_DOWN, -1, 0},
84 {2, 1, "Wine", FR_DOWN, -1, 0},
85 {30, 29, "Wine", FR_DOWN, -1, 0},
86 {20, 5, "Wine", 0, 13, 0},
89 {5, 10, "", FR_DOWN, -1, 0},
90 {10, 5, "", FR_DOWN, -1, 0},
91 {0, -1, "", FR_DOWN, -1, 0},
92 {10, 5, "", 0, -1, 0},
94 /* Whole-word search */
95 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
96 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
97 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
98 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
99 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
100 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
101 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
104 {5, 200, "XXX", FR_DOWN, -1, 0},
105 {-20, 20, "Wine", FR_DOWN, -1, 0},
106 {-20, 20, "Wine", FR_DOWN, -1, 0},
107 {-15, -20, "Wine", FR_DOWN, -1, 0},
108 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
110 /* Check the case noted in bug 4479 where matches at end aren't recognized */
111 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
112 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
113 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
114 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
115 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
117 /* The backwards case of bug 4479; bounds look right
118 * Fails because backward find is wrong */
119 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
120 {0, 20, "WINE", FR_MATCHCASE, -1, 0}
123 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
126 memset(&ft, 0, sizeof(ft));
127 ft.chrg.cpMin = f->start;
128 ft.chrg.cpMax = f->end;
129 ft.lpstrText = f->needle;
130 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
131 ok(findloc == f->expected_loc,
132 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
133 name, id, f->needle, f->start, f->end, f->flags, findloc);
136 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
140 memset(&ft, 0, sizeof(ft));
141 ft.chrg.cpMin = f->start;
142 ft.chrg.cpMax = f->end;
143 ft.lpstrText = f->needle;
144 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
145 ok(findloc == f->expected_loc,
146 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
147 name, id, f->needle, f->start, f->end, f->flags, findloc);
148 ok(ft.chrgText.cpMin == f->expected_loc,
149 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %ld\n",
150 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
151 ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
152 : f->expected_loc + strlen(f->needle)),
153 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %ld\n",
154 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
157 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
162 for (i = 0; i < num_tests; i++) {
163 if (find[i]._todo_wine) {
165 check_EM_FINDTEXT(hwnd, name, &find[i], i);
166 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
169 check_EM_FINDTEXT(hwnd, name, &find[i], i);
170 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
175 static void test_EM_FINDTEXT(void)
177 HWND hwndRichEdit = new_richedit(NULL);
179 /* Empty rich edit control */
180 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
181 sizeof(find_tests)/sizeof(struct find_s));
183 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
186 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
187 sizeof(find_tests2)/sizeof(struct find_s));
189 DestroyWindow(hwndRichEdit);
192 static const struct getline_s {
197 {0, 10, "foo bar\r"},
202 /* Buffer smaller than line length */
208 static void test_EM_GETLINE(void)
211 HWND hwndRichEdit = new_richedit(NULL);
212 static const int nBuf = 1024;
213 char dest[1024], origdest[1024];
214 const char text[] = "foo bar\n"
218 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
220 memset(origdest, 0xBB, nBuf);
221 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
224 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
225 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
226 memset(dest, 0xBB, nBuf);
227 *(WORD *) dest = gl[i].buffer_len;
229 /* EM_GETLINE appends a "\r\0" to the end of the line
230 * nCopied counts up to and including the '\r' */
231 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
232 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
234 /* two special cases since a parameter is passed via dest */
235 if (gl[i].buffer_len == 0)
236 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
238 else if (gl[i].buffer_len == 1)
239 ok(dest[0] == gl[i].text[0] && !dest[1] &&
240 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
243 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
244 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
245 ok(!strncmp(dest + expected_bytes_written, origdest
246 + expected_bytes_written, nBuf - expected_bytes_written),
247 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
251 DestroyWindow(hwndRichEdit);
254 static int get_scroll_pos_y(HWND hwnd)
257 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
258 ok(p.x != -1 && p.y != -1, "p.x:%ld p.y:%ld\n", p.x, p.y);
262 static void move_cursor(HWND hwnd, long charindex)
265 cr.cpMax = charindex;
266 cr.cpMin = charindex;
267 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
270 static void line_scroll(HWND hwnd, int amount)
272 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
275 static void test_EM_SCROLLCARET(void)
278 HWND hwndRichEdit = new_richedit(NULL);
279 const char text[] = "aa\n"
280 "this is a long line of text that should be longer than the "
289 /* Can't verify this */
290 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
292 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
294 /* Caret above visible window */
295 line_scroll(hwndRichEdit, 3);
296 prevY = get_scroll_pos_y(hwndRichEdit);
297 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
298 curY = get_scroll_pos_y(hwndRichEdit);
299 ok(prevY != curY, "%d == %d\n", prevY, curY);
301 /* Caret below visible window */
302 move_cursor(hwndRichEdit, sizeof(text) - 1);
303 line_scroll(hwndRichEdit, -3);
304 prevY = get_scroll_pos_y(hwndRichEdit);
305 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
306 curY = get_scroll_pos_y(hwndRichEdit);
307 ok(prevY != curY, "%d == %d\n", prevY, curY);
309 /* Caret in visible window */
310 move_cursor(hwndRichEdit, sizeof(text) - 2);
311 prevY = get_scroll_pos_y(hwndRichEdit);
312 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
313 curY = get_scroll_pos_y(hwndRichEdit);
314 ok(prevY == curY, "%d != %d\n", prevY, curY);
316 /* Caret still in visible window */
317 line_scroll(hwndRichEdit, -1);
318 prevY = get_scroll_pos_y(hwndRichEdit);
319 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
320 curY = get_scroll_pos_y(hwndRichEdit);
321 ok(prevY == curY, "%d != %d\n", prevY, curY);
323 DestroyWindow(hwndRichEdit);
326 static void test_EM_SETTEXTMODE(void)
328 HWND hwndRichEdit = new_richedit(NULL);
329 CHARFORMAT2 cf2, cf2test;
333 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
334 /*Insert text into the control*/
336 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
338 /*Attempt to change the control to plain text mode*/
339 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
340 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
342 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
343 If rich text is pasted, it should have the same formatting as the rest
344 of the text in the control*/
347 *NOTE: If the default text was already italicized, the test will simply
348 reverse; in other words, it will copy a regular "wine" into a plain
349 text window that uses an italicized format*/
350 cf2.cbSize = sizeof(CHARFORMAT2);
351 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
354 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
355 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
357 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
358 however, SCF_ALL has been implemented*/
359 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
360 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
362 /*Select the string "wine"*/
365 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
367 /*Copy the italicized "wine" to the clipboard*/
368 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
370 /*Reset the formatting to default*/
371 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
372 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
374 /*Clear the text in the control*/
375 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
377 /*Switch to Plain Text Mode*/
378 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
379 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
381 /*Input "wine" again in normal format*/
382 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
384 /*Paste the italicized "wine" into the control*/
385 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
387 /*Select a character from the first "wine" string*/
390 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
392 /*Retrieve its formatting*/
393 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
396 /*Select a character from the second "wine" string*/
399 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
401 /*Retrieve its formatting*/
402 cf2test.cbSize = sizeof(CHARFORMAT2);
403 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
406 /*Compare the two formattings*/
407 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
408 "two formats found in plain text mode - cf2.dwEffects: %lx cf2test.dwEffects: %lx\n",
409 cf2.dwEffects, cf2test.dwEffects);
410 /*Test TM_RICHTEXT by: switching back to Rich Text mode
411 printing "wine" in the current format(normal)
412 pasting "wine" from the clipboard(italicized)
413 comparing the two formats(should differ)*/
415 /*Attempt to switch with text in control*/
416 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
417 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
420 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
422 /*Switch into Rich Text mode*/
423 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
424 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
426 /*Print "wine" in normal formatting into the control*/
427 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
429 /*Paste italicized "wine" into the control*/
430 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
432 /*Select text from the first "wine" string*/
435 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
437 /*Retrieve its formatting*/
438 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
441 /*Select text from the second "wine" string*/
444 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
446 /*Retrieve its formatting*/
447 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
450 /*Test that the two formattings are not the same*/
451 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
452 "expected different formats - cf2.dwMask: %lx, cf2test.dwMask: %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
453 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
455 DestroyWindow(hwndRichEdit);
458 static void test_TM_PLAINTEXT()
460 /*Tests plain text properties*/
462 HWND hwndRichEdit = new_richedit(NULL);
463 CHARFORMAT2 cf2, cf2test;
466 /*Switch to plain text mode*/
468 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
469 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
471 /*Fill control with text*/
473 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
475 /*Select some text and bold it*/
479 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
480 cf2.cbSize = sizeof(CHARFORMAT2);
481 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
484 cf2.dwMask = CFM_BOLD | cf2.dwMask;
485 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
487 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
489 /*Get the formatting of those characters*/
491 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
493 /*Get the formatting of some other characters*/
494 cf2test.cbSize = sizeof(CHARFORMAT2);
497 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
498 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
500 /*Test that they are the same as plain text allows only one formatting*/
502 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
503 "two selections' formats differ - cf2.dwMask: %lx, cf2test.dwMask %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
504 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
506 /*Fill the control with a "wine" string, which when inserted will be bold*/
508 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
510 /*Copy the bolded "wine" string*/
514 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
515 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
517 /*Swap back to rich text*/
519 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
520 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
522 /*Set the default formatting to bold italics*/
524 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
525 cf2.dwMask |= CFM_ITALIC;
526 cf2.dwEffects ^= CFE_ITALIC;
527 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
529 /*Set the text in the control to "wine", which will be bold and italicized*/
531 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
533 /*Paste the plain text "wine" string, which should take the insert
534 formatting, which at the moment is bold italics*/
536 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
538 /*Select the first "wine" string and retrieve its formatting*/
542 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
543 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
545 /*Select the second "wine" string and retrieve its formatting*/
549 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
550 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
552 /*Compare the two formattings. They should be the same.*/
554 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
555 "Copied text retained formatting - cf2.dwMask: %lx, cf2test.dwMask: %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
556 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
557 DestroyWindow(hwndRichEdit);
560 static void test_WM_GETTEXT()
562 HWND hwndRichEdit = new_richedit(NULL);
563 static const char text[] = "Hello. My name is RichEdit!";
564 char buffer[1024] = {0};
567 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
568 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
569 result = strcmp(buffer,text);
571 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
574 /* FIXME: need to test unimplemented options and robustly test wparam */
575 static void test_EM_SETOPTIONS()
577 HWND hwndRichEdit = new_richedit(NULL);
578 static const char text[] = "Hello. My name is RichEdit!";
579 char buffer[1024] = {0};
581 /* NEGATIVE TESTING - NO OPTIONS SET */
582 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
583 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
585 /* testing no readonly by sending 'a' to the control*/
586 SetFocus(hwndRichEdit);
587 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
588 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
590 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
591 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
593 /* READONLY - sending 'a' to the control */
594 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
595 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
596 SetFocus(hwndRichEdit);
597 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
598 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
599 ok(buffer[0]==text[0],
600 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
602 DestroyWindow(hwndRichEdit);
605 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
607 CHARFORMAT2W text_format;
608 int link_present = 0;
609 text_format.cbSize = sizeof(text_format);
610 SendMessage(hwnd, EM_SETSEL, 0, 0);
611 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
612 link_present = text_format.dwEffects & CFE_LINK;
614 { /* control text is url; should get CFE_LINK */
615 ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
619 ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
623 static HWND new_static_wnd(HWND parent) {
624 return new_window("Static", 0, parent);
627 static void test_EM_AUTOURLDETECT(void)
634 {"http://www.winehq.org", 1},
635 {"http//winehq.org", 0},
636 {"ww.winehq.org", 0},
637 {"www.winehq.org", 1},
638 {"ftp://192.168.1.1", 1},
639 {"ftp//192.168.1.1", 0},
640 {"mailto:your@email.com", 1},
641 {"prospero:prosperoserver", 1},
643 {"news:newserver", 1},
644 {"wais:waisserver", 1}
649 HWND hwndRichEdit, parent;
651 parent = new_static_wnd(NULL);
652 hwndRichEdit = new_richedit(parent);
653 /* Try and pass EM_AUTOURLDETECT some test wParam values */
654 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
655 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
656 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
657 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
658 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
659 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
660 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
661 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
662 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
663 /* for each url, check the text to see if CFE_LINK effect is present */
664 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
665 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
666 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
667 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
668 check_CFE_LINK_rcvd(hwndRichEdit, 0);
669 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
670 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
671 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
672 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
674 DestroyWindow(hwndRichEdit);
675 DestroyWindow(parent);
678 static void test_EM_SCROLL()
681 int r; /* return value */
682 int expr; /* expected return value */
683 HWND hwndRichEdit = new_richedit(NULL);
684 int y_before, y_after; /* units of lines of text */
686 /* test a richedit box containing a single line of text */
687 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
689 for (i = 0; i < 4; i++) {
690 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
692 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
693 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
694 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
695 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
696 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
697 "(i == %d)\n", y_after, i);
701 * test a richedit box that will scroll. There are two general
702 * cases: the case without any long lines and the case with a long
705 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
707 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
709 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
710 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
711 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
712 "LONG LINE \nb\nc\nd\ne");
713 for (j = 0; j < 12; j++) /* reset scrol position to top */
714 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
716 /* get first visible line */
717 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
718 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
720 /* get new current first visible line */
721 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
723 ok(((r & 0xffffff00) == 0x00010000) &&
724 ((r & 0x000000ff) != 0x00000000),
725 "EM_SCROLL page down didn't scroll by a small positive number of "
726 "lines (r == 0x%08x)\n", r);
727 ok(y_after > y_before, "EM_SCROLL page down not functioning "
728 "(line %d scrolled to line %d\n", y_before, y_after);
732 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
733 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
734 ok(((r & 0xffffff00) == 0x0001ff00),
735 "EM_SCROLL page up didn't scroll by a small negative number of lines "
736 "(r == 0x%08x)\n", r);
737 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
738 "%d scrolled to line %d\n", y_before, y_after);
742 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
744 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
746 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
747 "(r == 0x%08x)\n", r);
748 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
749 "1 line (%d scrolled to %d)\n", y_before, y_after);
753 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
755 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
757 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
758 "(r == 0x%08x)\n", r);
759 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
760 "line (%d scrolled to %d)\n", y_before, y_after);
764 r = SendMessage(hwndRichEdit, EM_SCROLL,
765 SB_LINEUP, 0); /* lineup beyond top */
767 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
770 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
771 ok(y_before == y_after,
772 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
776 r = SendMessage(hwndRichEdit, EM_SCROLL,
777 SB_PAGEUP, 0);/*page up beyond top */
779 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
782 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
783 ok(y_before == y_after,
784 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
786 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
787 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
788 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
789 r = SendMessage(hwndRichEdit, EM_SCROLL,
790 SB_PAGEDOWN, 0); /* page down beyond bot */
791 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
794 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
795 ok(y_before == y_after,
796 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
799 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
800 SendMessage(hwndRichEdit, EM_SCROLL,
801 SB_LINEDOWN, 0); /* line down beyond bot */
802 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
805 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
806 ok(y_before == y_after,
807 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
810 DestroyWindow(hwndRichEdit);
813 static void test_EM_SETUNDOLIMIT()
815 /* cases we test for:
816 * default behaviour - limiting at 100 undo's
817 * undo disabled - setting a limit of 0
818 * undo limited - undo limit set to some to some number, like 2
819 * bad input - sending a negative number should default to 100 undo's */
821 HWND hwndRichEdit = new_richedit(NULL);
826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
829 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
830 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
831 also, multiple pastes don't combine like WM_CHAR would */
832 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
834 /* first case - check the default */
835 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
836 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
837 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
838 for (i=0; i<100; i++) /* Undo 100 of them */
839 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
840 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
841 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
843 /* second case - cannot undo */
844 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
845 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
846 SendMessage(hwndRichEdit,
847 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
848 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
849 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
851 /* third case - set it to an arbitrary number */
852 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
853 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
854 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
855 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
856 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
857 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
858 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
859 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
860 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
861 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
862 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
863 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
864 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
865 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
867 /* fourth case - setting negative numbers should default to 100 undos */
868 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
869 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
871 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
873 DestroyWindow(hwndRichEdit);
876 static void test_ES_PASSWORD()
878 /* This isn't hugely testable, so we're just going to run it through it's paces. */
880 HWND hwndRichEdit = new_richedit(NULL);
883 /* First, check the default of a regular control */
884 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
886 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
888 /* Now, set it to something normal */
889 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
890 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
892 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
894 /* Now, set it to something odd */
895 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
896 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
898 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
901 static void test_EM_SETTEXTEX()
903 HWND hwndRichEdit = new_richedit(NULL);
906 WCHAR TestItem1[] = {'T', 'e', 's', 't',
908 'T', 'e', 'x', 't', 0};
909 #define MAX_BUF_LEN 1024
910 WCHAR buf[MAX_BUF_LEN];
914 setText.codepage = 1200; /* no constant for unicode */
915 getText.codepage = 1200; /* no constant for unicode */
916 getText.cb = MAX_BUF_LEN;
917 getText.flags = GT_DEFAULT;
920 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
921 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
922 ok(lstrcmpW(buf, TestItem1) == 0,
923 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
925 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
926 (WPARAM)&setText, (LPARAM) NULL);
927 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
930 "EM_SETTEXTEX returned %d, instead of 1\n",result);
931 ok(lstrlenW(buf) == 0,
932 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
934 /* put some text back */
936 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
937 /* select some text */
940 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
941 /* replace current selection */
942 setText.flags = ST_SELECTION;
943 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
944 (WPARAM)&setText, (LPARAM) NULL);
946 "EM_SETTEXTEX with NULL lParam to replace selection"
947 " with no text should return 0. Got %i\n",
950 /* put some text back */
952 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
953 /* select some text */
956 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
957 /* replace current selection */
958 setText.flags = ST_SELECTION;
959 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
960 (WPARAM)&setText, (LPARAM) TestItem1);
962 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
963 ok(result == lstrlenW(TestItem1),
964 "EM_SETTEXTEX with NULL lParam to replace selection"
965 " with no text should return 0. Got %i\n",
967 ok(lstrlenW(buf) == 22,
968 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
971 DestroyWindow(hwndRichEdit);
980 /* Must explicitly LoadLibrary(). The test has no references to functions in
981 * RICHED20.DLL, so the linker doesn't actually link to it. */
982 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
983 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
986 test_EM_SCROLLCARET();
988 test_EM_SETTEXTMODE();
990 test_EM_SETOPTIONS();
992 test_EM_AUTOURLDETECT();
993 test_EM_SETUNDOLIMIT();
997 /* Set the environment variable WINETEST_RICHED20 to keep windows
998 * responsive and open for 30 seconds. This is useful for debugging.
1000 * The message pump uses PeekMessage() to empty the queue and then sleeps for
1001 * 50ms before retrying the queue. */
1002 end = time(NULL) + 30;
1003 if (getenv( "WINETEST_RICHED20" )) {
1004 while (time(NULL) < end) {
1005 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1006 TranslateMessage(&msg);
1007 DispatchMessage(&msg);
1014 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());