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, 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, 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, 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 int get_scroll_pos_y(HWND hwnd)
195 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
196 ok(p.x != -1 && p.y != -1, "p.x:%ld p.y:%ld\n", p.x, p.y);
200 static void move_cursor(HWND hwnd, long charindex)
203 cr.cpMax = charindex;
204 cr.cpMin = charindex;
205 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
208 static void line_scroll(HWND hwnd, int amount)
210 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
213 static void test_EM_SCROLLCARET(void)
216 HWND hwndRichEdit = new_richedit(NULL);
217 const char text[] = "aa\n"
218 "this is a long line of text that should be longer than the "
227 /* Can't verify this */
228 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
230 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
232 /* Caret above visible window */
233 line_scroll(hwndRichEdit, 3);
234 prevY = get_scroll_pos_y(hwndRichEdit);
235 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
236 curY = get_scroll_pos_y(hwndRichEdit);
237 ok(prevY != curY, "%d == %d\n", prevY, curY);
239 /* Caret below visible window */
240 move_cursor(hwndRichEdit, sizeof(text) - 1);
241 line_scroll(hwndRichEdit, -3);
242 prevY = get_scroll_pos_y(hwndRichEdit);
243 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
244 curY = get_scroll_pos_y(hwndRichEdit);
245 ok(prevY != curY, "%d == %d\n", prevY, curY);
247 /* Caret in visible window */
248 move_cursor(hwndRichEdit, sizeof(text) - 2);
249 prevY = get_scroll_pos_y(hwndRichEdit);
250 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
251 curY = get_scroll_pos_y(hwndRichEdit);
252 ok(prevY == curY, "%d != %d\n", prevY, curY);
254 /* Caret still in visible window */
255 line_scroll(hwndRichEdit, -1);
256 prevY = get_scroll_pos_y(hwndRichEdit);
257 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
258 curY = get_scroll_pos_y(hwndRichEdit);
259 ok(prevY == curY, "%d != %d\n", prevY, curY);
261 DestroyWindow(hwndRichEdit);
264 static void test_EM_SETTEXTMODE(void)
266 HWND hwndRichEdit = new_richedit(NULL);
267 CHARFORMAT2 cf2, cf2test;
271 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
272 /*Insert text into the control*/
274 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
276 /*Attempt to change the control to plain text mode*/
277 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
278 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
280 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
281 If rich text is pasted, it should have the same formatting as the rest
282 of the text in the control*/
285 *NOTE: If the default text was already italicized, the test will simply
286 reverse; in other words, it will copy a regular "wine" into a plain
287 text window that uses an italicized format*/
288 cf2.cbSize = sizeof(CHARFORMAT2);
289 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
292 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
293 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
295 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
296 however, SCF_ALL has been implemented*/
297 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
298 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
300 /*Select the string "wine"*/
303 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
305 /*Copy the italicized "wine" to the clipboard*/
306 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
308 /*Reset the formatting to default*/
309 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
310 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
312 /*Clear the text in the control*/
313 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
315 /*Switch to Plain Text Mode*/
316 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
317 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
319 /*Input "wine" again in normal format*/
320 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
322 /*Paste the italicized "wine" into the control*/
323 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
325 /*Select a character from the first "wine" string*/
328 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
330 /*Retrieve its formatting*/
331 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
334 /*Select a character from the second "wine" string*/
337 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
339 /*Retrieve its formatting*/
340 cf2test.cbSize = sizeof(CHARFORMAT2);
341 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
344 /*Compare the two formattings*/
345 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
346 "two formats found in plain text mode - cf2.dwEffects: %lx cf2test.dwEffects: %lx\n",
347 cf2.dwEffects, cf2test.dwEffects);
348 /*Test TM_RICHTEXT by: switching back to Rich Text mode
349 printing "wine" in the current format(normal)
350 pasting "wine" from the clipboard(italicized)
351 comparing the two formats(should differ)*/
353 /*Attempt to switch with text in control*/
354 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
355 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
358 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
360 /*Switch into Rich Text mode*/
361 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
362 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
364 /*Print "wine" in normal formatting into the control*/
365 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
367 /*Paste italicized "wine" into the control*/
368 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
370 /*Select text from the first "wine" string*/
373 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
375 /*Retrieve its formatting*/
376 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
379 /*Select text from the second "wine" string*/
382 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
384 /*Retrieve its formatting*/
385 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
388 /*Test that the two formattings are not the same*/
389 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
390 "expected different formats - cf2.dwMask: %lx, cf2test.dwMask: %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
391 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
393 DestroyWindow(hwndRichEdit);
396 static void test_TM_PLAINTEXT()
398 /*Tests plain text properties*/
400 HWND hwndRichEdit = new_richedit(NULL);
401 CHARFORMAT2 cf2, cf2test;
404 /*Switch to plain text mode*/
406 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
407 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
409 /*Fill control with text*/
411 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
413 /*Select some text and bold it*/
417 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
418 cf2.cbSize = sizeof(CHARFORMAT2);
419 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
422 cf2.dwMask = CFM_BOLD | cf2.dwMask;
423 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
425 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
427 /*Get the formatting of those characters*/
429 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
431 /*Get the formatting of some other characters*/
432 cf2test.cbSize = sizeof(CHARFORMAT2);
435 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
436 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
438 /*Test that they are the same as plain text allows only one formatting*/
440 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
441 "two selections' formats differ - cf2.dwMask: %lx, cf2test.dwMask %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
442 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
444 /*Fill the control with a "wine" string, which when inserted will be bold*/
446 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
448 /*Copy the bolded "wine" string*/
452 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
453 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
455 /*Swap back to rich text*/
457 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
458 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
460 /*Set the default formatting to bold italics*/
462 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
463 cf2.dwMask |= CFM_ITALIC;
464 cf2.dwEffects ^= CFE_ITALIC;
465 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
467 /*Set the text in the control to "wine", which will be bold and italicized*/
469 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
471 /*Paste the plain text "wine" string, which should take the insert
472 formatting, which at the moment is bold italics*/
474 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
476 /*Select the first "wine" string and retrieve its formatting*/
480 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
481 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
483 /*Select the second "wine" string and retrieve its formatting*/
487 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
488 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
490 /*Compare the two formattings. They should be the same.*/
492 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
493 "Copied text retained formatting - cf2.dwMask: %lx, cf2test.dwMask: %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
494 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
495 DestroyWindow(hwndRichEdit);
498 static void test_WM_GETTEXT()
500 HWND hwndRichEdit = new_richedit(NULL);
501 static const char text[] = "Hello. My name is RichEdit!";
502 char buffer[1024] = {0};
505 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
506 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
507 result = strcmp(buffer,text);
509 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
512 /* FIXME: need to test unimplemented options and robustly test wparam */
513 static void test_EM_SETOPTIONS()
515 HWND hwndRichEdit = new_richedit(NULL);
516 static const char text[] = "Hello. My name is RichEdit!";
517 char buffer[1024] = {0};
519 /* NEGATIVE TESTING - NO OPTIONS SET */
520 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
521 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
523 /* testing no readonly by sending 'a' to the control*/
524 SetFocus(hwndRichEdit);
525 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
526 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
528 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
529 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
531 /* READONLY - sending 'a' to the control */
532 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
533 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
534 SetFocus(hwndRichEdit);
535 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
536 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
537 ok(buffer[0]==text[0],
538 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
540 DestroyWindow(hwndRichEdit);
543 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
545 CHARFORMAT2W text_format;
546 int link_present = 0;
547 text_format.cbSize = sizeof(text_format);
548 SendMessage(hwnd, EM_SETSEL, 0, 0);
549 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
550 link_present = text_format.dwEffects & CFE_LINK;
552 { /* control text is url; should get CFE_LINK */
553 ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
557 ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
561 static HWND new_static_wnd(HWND parent) {
562 return new_window("Static", 0, parent);
565 static void test_EM_AUTOURLDETECT(void)
572 {"http://www.winehq.org", 1},
573 {"http//winehq.org", 0},
574 {"ww.winehq.org", 0},
575 {"www.winehq.org", 1},
576 {"ftp://192.168.1.1", 1},
577 {"ftp//192.168.1.1", 0},
578 {"mailto:your@email.com", 1},
579 {"prospero:prosperoserver", 1},
581 {"news:newserver", 1},
582 {"wais:waisserver", 1}
587 HWND hwndRichEdit, parent;
589 parent = new_static_wnd(NULL);
590 hwndRichEdit = new_richedit(parent);
591 /* Try and pass EM_AUTOURLDETECT some test wParam values */
592 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
593 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
594 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
595 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
596 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
597 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
598 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
599 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
600 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
601 /* for each url, check the text to see if CFE_LINK effect is present */
602 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
603 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
604 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
605 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
606 check_CFE_LINK_rcvd(hwndRichEdit, 0);
607 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
608 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
609 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
610 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
612 DestroyWindow(hwndRichEdit);
613 DestroyWindow(parent);
616 static void test_EM_SCROLL()
619 int r; /* return value */
620 int expr; /* expected return value */
621 HWND hwndRichEdit = new_richedit(NULL);
622 int y_before, y_after; /* units of lines of text */
624 /* test a richedit box containing a single line of text */
625 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
627 for (i = 0; i < 4; i++) {
628 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
630 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
631 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
632 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
633 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
634 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
635 "(i == %d)\n", y_after, i);
639 * test a richedit box that will scroll. There are two general
640 * cases: the case without any long lines and the case with a long
643 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
645 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
647 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
648 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
649 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
650 "LONG LINE \nb\nc\nd\ne");
651 for (j = 0; j < 12; j++) /* reset scrol position to top */
652 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
654 /* get first visible line */
655 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
656 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
658 /* get new current first visible line */
659 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
661 ok(((r & 0xffffff00) == 0x00010000) &&
662 ((r & 0x000000ff) != 0x00000000),
663 "EM_SCROLL page down didn't scroll by a small positive number of "
664 "lines (r == 0x%08x)\n", r);
665 ok(y_after > y_before, "EM_SCROLL page down not functioning "
666 "(line %d scrolled to line %d\n", y_before, y_after);
670 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
671 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
672 ok(((r & 0xffffff00) == 0x0001ff00),
673 "EM_SCROLL page up didn't scroll by a small negative number of lines "
674 "(r == 0x%08x)\n", r);
675 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
676 "%d scrolled to line %d\n", y_before, y_after);
680 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
682 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
684 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
685 "(r == 0x%08x)\n", r);
686 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
687 "1 line (%d scrolled to %d)\n", y_before, y_after);
691 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
693 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
695 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
696 "(r == 0x%08x)\n", r);
697 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
698 "line (%d scrolled to %d)\n", y_before, y_after);
702 r = SendMessage(hwndRichEdit, EM_SCROLL,
703 SB_LINEUP, 0); /* lineup beyond top */
705 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
708 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
709 ok(y_before == y_after,
710 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
714 r = SendMessage(hwndRichEdit, EM_SCROLL,
715 SB_PAGEUP, 0);/*page up beyond top */
717 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
720 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
721 ok(y_before == y_after,
722 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
724 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
725 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
726 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
727 r = SendMessage(hwndRichEdit, EM_SCROLL,
728 SB_PAGEDOWN, 0); /* page down beyond bot */
729 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
732 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
733 ok(y_before == y_after,
734 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
737 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
738 SendMessage(hwndRichEdit, EM_SCROLL,
739 SB_LINEDOWN, 0); /* line down beyond bot */
740 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
743 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
744 ok(y_before == y_after,
745 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
748 DestroyWindow(hwndRichEdit);
751 static void test_EM_SETUNDOLIMIT()
753 /* cases we test for:
754 * default behaviour - limiting at 100 undo's
755 * undo disabled - setting a limit of 0
756 * undo limited - undo limit set to some to some number, like 2
757 * bad input - sending a negative number should default to 100 undo's */
759 HWND hwndRichEdit = new_richedit(NULL);
764 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
767 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
768 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
769 also, multiple pastes don't combine like WM_CHAR would */
770 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
772 /* first case - check the default */
773 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
774 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
775 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
776 for (i=0; i<100; i++) /* Undo 100 of them */
777 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
778 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
779 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
781 /* second case - cannot undo */
782 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
783 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
784 SendMessage(hwndRichEdit,
785 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
786 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
787 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
789 /* third case - set it to an arbitrary number */
790 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
791 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
792 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
793 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
794 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
795 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
796 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
797 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
798 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
799 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
800 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
801 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
802 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
803 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
805 /* fourth case - setting negative numbers should default to 100 undos */
806 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
807 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
809 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
811 DestroyWindow(hwndRichEdit);
820 /* Must explicitly LoadLibrary(). The test has no references to functions in
821 * RICHED20.DLL, so the linker doesn't actually link to it. */
822 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
823 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
825 test_EM_SCROLLCARET();
827 test_EM_SETTEXTMODE();
829 test_EM_SETOPTIONS();
831 test_EM_AUTOURLDETECT();
832 test_EM_SETUNDOLIMIT();
834 /* Set the environment variable WINETEST_RICHED20 to keep windows
835 * responsive and open for 30 seconds. This is useful for debugging.
837 * The message pump uses PeekMessage() to empty the queue and then sleeps for
838 * 50ms before retrying the queue. */
839 end = time(NULL) + 30;
840 if (getenv( "WINETEST_RICHED20" )) {
841 while (time(NULL) < end) {
842 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
843 TranslateMessage(&msg);
844 DispatchMessage(&msg);
851 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());