Update the address of the Free Software Foundation.
[wine/testsucceed.git] / dlls / riched20 / tests / editor.c
blob8dca7d9aaa87838a34f7a73c2507bbfe3f952252
1 /*
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>
22 #include <windows.h>
23 #include <richedit.h>
24 #include <time.h>
26 static HMODULE hmoduleRichEdit;
28 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
29 HWND hwnd;
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());
34 return hwnd;
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";
42 /* ^0 ^10 ^20 ^30 */
44 struct find_s {
45 int start;
46 int end;
47 char *needle;
48 int flags;
49 int expected_loc;
50 int _todo_wine;
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[] = {
63 /* No-result find */
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},
73 /* Find backwards */
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},
88 /* Find nothing */
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},
103 /* Bad ranges */
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) {
124 int findloc;
125 FINDTEXT ft;
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,
137 int id) {
138 int findloc;
139 FINDTEXTEX ft;
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,
158 int num_tests)
160 int i;
162 for (i = 0; i < num_tests; i++) {
163 if (find[i]._todo_wine) {
164 todo_wine {
165 check_EM_FINDTEXT(hwnd, name, &find[i], i);
166 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
168 } else {
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);
185 /* Haystack text */
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)
194 POINT p = {-1, -1};
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);
197 return p.y;
200 static void move_cursor(HWND hwnd, long charindex)
202 CHARRANGE cr;
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)
215 int prevY, curY;
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 "
219 "control's width\n"
220 "cc\n"
221 "dd\n"
222 "ee\n"
223 "ff\n"
224 "gg\n"
225 "hh\n";
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;
268 CHARRANGE cr;
269 int rc = 0;
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*/
284 /*Italicize the text
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,
290 (LPARAM) &cf2);
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"*/
301 cr.cpMin = 0;
302 cr.cpMax = 4;
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*/
326 cr.cpMin = 2;
327 cr.cpMax = 3;
328 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
330 /*Retrieve its formatting*/
331 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
332 (LPARAM) &cf2);
334 /*Select a character from the second "wine" string*/
335 cr.cpMin = 5;
336 cr.cpMax = 6;
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,
342 (LPARAM) &cf2test);
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: %f cf2test.dwEffects: %f\n",(double) cf2.dwEffects, (double) cf2test.dwEffects);
347 /*Test TM_RICHTEXT by: switching back to Rich Text mode
348 printing "wine" in the current format(normal)
349 pasting "wine" from the clipboard(italicized)
350 comparing the two formats(should differ)*/
352 /*Attempt to switch with text in control*/
353 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
354 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
356 /*Clear control*/
357 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
359 /*Switch into Rich Text mode*/
360 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
361 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
363 /*Print "wine" in normal formatting into the control*/
364 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
366 /*Paste italicized "wine" into the control*/
367 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
369 /*Select text from the first "wine" string*/
370 cr.cpMin = 1;
371 cr.cpMax = 3;
372 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
374 /*Retrieve its formatting*/
375 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
376 (LPARAM) &cf2);
378 /*Select text from the second "wine" string*/
379 cr.cpMin = 6;
380 cr.cpMax = 7;
381 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
383 /*Retrieve its formatting*/
384 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
385 (LPARAM) &cf2test);
387 /*Test that the two formattings are not the same*/
388 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
389 "expected different formats - cf2.dwMask: %lx, cf2test.dwMask: %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
390 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
392 DestroyWindow(hwndRichEdit);
395 static void test_TM_PLAINTEXT()
397 /*Tests plain text properties*/
399 HWND hwndRichEdit = new_richedit(NULL);
400 CHARFORMAT2 cf2, cf2test;
401 CHARRANGE cr;
403 /*Switch to plain text mode*/
405 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
406 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
408 /*Fill control with text*/
410 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
412 /*Select some text and bold it*/
414 cr.cpMin = 10;
415 cr.cpMax = 20;
416 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
417 cf2.cbSize = sizeof(CHARFORMAT2);
418 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
419 (LPARAM) &cf2);
421 cf2.dwMask = CFM_BOLD | cf2.dwMask;
422 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
424 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
426 /*Get the formatting of those characters*/
428 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
430 /*Get the formatting of some other characters*/
431 cf2test.cbSize = sizeof(CHARFORMAT2);
432 cr.cpMin = 21;
433 cr.cpMax = 30;
434 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
435 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
437 /*Test that they are the same as plain text allows only one formatting*/
439 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
440 "two selections' formats differ - cf2.dwMask: %lx, cf2test.dwMask %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
441 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
443 /*Fill the control with a "wine" string, which when inserted will be bold*/
445 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
447 /*Copy the bolded "wine" string*/
449 cr.cpMin = 0;
450 cr.cpMax = 4;
451 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
452 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
454 /*Swap back to rich text*/
456 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
457 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
459 /*Set the default formatting to bold italics*/
461 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
462 cf2.dwMask |= CFM_ITALIC;
463 cf2.dwEffects ^= CFE_ITALIC;
464 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
466 /*Set the text in the control to "wine", which will be bold and italicized*/
468 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
470 /*Paste the plain text "wine" string, which should take the insert
471 formatting, which at the moment is bold italics*/
473 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
475 /*Select the first "wine" string and retrieve its formatting*/
477 cr.cpMin = 1;
478 cr.cpMax = 3;
479 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
480 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
482 /*Select the second "wine" string and retrieve its formatting*/
484 cr.cpMin = 5;
485 cr.cpMax = 7;
486 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
487 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
489 /*Compare the two formattings. They should be the same.*/
491 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
492 "Copied text retained formatting - cf2.dwMask: %lx, cf2test.dwMask: %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
493 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
494 DestroyWindow(hwndRichEdit);
497 static void test_WM_GETTEXT()
499 HWND hwndRichEdit = new_richedit(NULL);
500 static const char text[] = "Hello. My name is RichEdit!";
501 char buffer[1024] = {0};
502 int result;
504 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
505 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
506 result = strcmp(buffer,text);
507 ok(result == 0,
508 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
511 /* FIXME: need to test unimplemented options and robustly test wparam */
512 static void test_EM_SETOPTIONS()
514 HWND hwndRichEdit = new_richedit(NULL);
515 static const char text[] = "Hello. My name is RichEdit!";
516 char buffer[1024] = {0};
518 /* NEGATIVE TESTING - NO OPTIONS SET */
519 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
520 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
522 /* testing no readonly by sending 'a' to the control*/
523 SetFocus(hwndRichEdit);
524 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
525 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
526 ok(buffer[0]=='a',
527 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
528 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
530 /* READONLY - sending 'a' to the control */
531 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
532 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
533 SetFocus(hwndRichEdit);
534 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
535 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
536 ok(buffer[0]==text[0],
537 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
539 DestroyWindow(hwndRichEdit);
542 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
544 CHARFORMAT2W text_format;
545 int link_present = 0;
546 text_format.cbSize = sizeof(text_format);
547 SendMessage(hwnd, EM_SETSEL, 0, 0);
548 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
549 link_present = text_format.dwEffects & CFE_LINK;
550 if (is_url)
551 { /* control text is url; should get CFE_LINK */
552 ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
554 else
556 ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
560 static HWND new_static_wnd(HWND parent) {
561 return new_window("Static", 0, parent);
564 static void test_EM_AUTOURLDETECT(void)
566 struct urls_s {
567 char *text;
568 int is_url;
569 } urls[12] = {
570 {"winehq.org", 0},
571 {"http://www.winehq.org", 1},
572 {"http//winehq.org", 0},
573 {"ww.winehq.org", 0},
574 {"www.winehq.org", 1},
575 {"ftp://192.168.1.1", 1},
576 {"ftp//192.168.1.1", 0},
577 {"mailto:your@email.com", 1},
578 {"prospero:prosperoserver", 1},
579 {"telnet:test", 1},
580 {"news:newserver", 1},
581 {"wais:waisserver", 1}
584 int i;
585 int urlRet=-1;
586 HWND hwndRichEdit, parent;
588 parent = new_static_wnd(NULL);
589 hwndRichEdit = new_richedit(parent);
590 /* Try and pass EM_AUTOURLDETECT some test wParam values */
591 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
592 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
593 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
594 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
595 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
596 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
597 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
598 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
599 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
600 /* for each url, check the text to see if CFE_LINK effect is present */
601 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
602 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
603 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
604 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
605 check_CFE_LINK_rcvd(hwndRichEdit, 0);
606 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
607 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
608 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
609 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
611 DestroyWindow(hwndRichEdit);
612 DestroyWindow(parent);
615 static void test_EM_SCROLL()
617 int i, j;
618 int r; /* return value */
619 int expr; /* expected return value */
620 HWND hwndRichEdit = new_richedit(NULL);
621 int y_before, y_after; /* units of lines of text */
623 /* test a richedit box containing a single line of text */
624 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
625 expr = 0x00010000;
626 for (i = 0; i < 4; i++) {
627 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
629 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
630 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
631 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
632 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
633 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
634 "(i == %d)\n", y_after, i);
638 * test a richedit box that will scroll. There are two general
639 * cases: the case without any long lines and the case with a long
640 * line.
642 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
643 if (i == 0)
644 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
645 else
646 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
647 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
648 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
649 "LONG LINE \nb\nc\nd\ne");
650 for (j = 0; j < 12; j++) /* reset scrol position to top */
651 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
653 /* get first visible line */
654 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
655 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
657 /* get new current first visible line */
658 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
660 ok(((r & 0xffffff00) == 0x00010000) &&
661 ((r & 0x000000ff) != 0x00000000),
662 "EM_SCROLL page down didn't scroll by a small positive number of "
663 "lines (r == 0x%08x)\n", r);
664 ok(y_after > y_before, "EM_SCROLL page down not functioning "
665 "(line %d scrolled to line %d\n", y_before, y_after);
667 y_before = y_after;
669 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
670 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
671 ok(((r & 0xffffff00) == 0x0001ff00),
672 "EM_SCROLL page up didn't scroll by a small negative number of lines "
673 "(r == 0x%08x)\n", r);
674 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
675 "%d scrolled to line %d\n", y_before, y_after);
677 y_before = y_after;
679 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
681 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
683 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
684 "(r == 0x%08x)\n", r);
685 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
686 "1 line (%d scrolled to %d)\n", y_before, y_after);
688 y_before = y_after;
690 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
692 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
694 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
695 "(r == 0x%08x)\n", r);
696 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
697 "line (%d scrolled to %d)\n", y_before, y_after);
699 y_before = y_after;
701 r = SendMessage(hwndRichEdit, EM_SCROLL,
702 SB_LINEUP, 0); /* lineup beyond top */
704 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
706 ok(r == 0x00010000,
707 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
708 ok(y_before == y_after,
709 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
711 y_before = y_after;
713 r = SendMessage(hwndRichEdit, EM_SCROLL,
714 SB_PAGEUP, 0);/*page up beyond top */
716 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
718 ok(r == 0x00010000,
719 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
720 ok(y_before == y_after,
721 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
723 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
724 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
725 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
726 r = SendMessage(hwndRichEdit, EM_SCROLL,
727 SB_PAGEDOWN, 0); /* page down beyond bot */
728 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
730 ok(r == 0x00010000,
731 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
732 ok(y_before == y_after,
733 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
734 y_before, y_after);
736 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
737 SendMessage(hwndRichEdit, EM_SCROLL,
738 SB_LINEDOWN, 0); /* line down beyond bot */
739 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
741 ok(r == 0x00010000,
742 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
743 ok(y_before == y_after,
744 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
745 y_before, y_after);
747 DestroyWindow(hwndRichEdit);
750 static void test_EM_SETUNDOLIMIT()
752 /* cases we test for:
753 * default behaviour - limiting at 100 undo's
754 * undo disabled - setting a limit of 0
755 * undo limited - undo limit set to some to some number, like 2
756 * bad input - sending a negative number should default to 100 undo's */
758 HWND hwndRichEdit = new_richedit(NULL);
759 CHARRANGE cr;
760 int i;
761 int result;
763 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
764 cr.cpMin = 0;
765 cr.cpMax = 1;
766 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
767 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
768 also, multiple pastes don't combine like WM_CHAR would */
769 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
771 /* first case - check the default */
772 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
773 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
774 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
775 for (i=0; i<100; i++) /* Undo 100 of them */
776 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
777 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
778 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
780 /* second case - cannot undo */
781 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
782 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
783 SendMessage(hwndRichEdit,
784 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
785 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
786 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
788 /* third case - set it to an arbitrary number */
789 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
790 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
791 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
792 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
793 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
794 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
795 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
796 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
797 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
798 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
799 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
800 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
801 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
802 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
804 /* fourth case - setting negative numbers should default to 100 undos */
805 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
806 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
807 ok (result == 100,
808 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100",result);
810 DestroyWindow(hwndRichEdit);
814 START_TEST( editor )
816 MSG msg;
817 time_t end;
819 /* Must explicitly LoadLibrary(). The test has no references to functions in
820 * RICHED20.DLL, so the linker doesn't actually link to it. */
821 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
822 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
823 test_EM_FINDTEXT();
824 test_EM_SCROLLCARET();
825 test_EM_SCROLL();
826 test_EM_SETTEXTMODE();
827 test_TM_PLAINTEXT();
828 test_EM_SETOPTIONS();
829 test_WM_GETTEXT();
830 test_EM_AUTOURLDETECT();
831 test_EM_SETUNDOLIMIT();
833 /* Set the environment variable WINETEST_RICHED20 to keep windows
834 * responsive and open for 30 seconds. This is useful for debugging.
836 * The message pump uses PeekMessage() to empty the queue and then sleeps for
837 * 50ms before retrying the queue. */
838 end = time(NULL) + 30;
839 if (getenv( "WINETEST_RICHED20" )) {
840 while (time(NULL) < end) {
841 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
842 TranslateMessage(&msg);
843 DispatchMessage(&msg);
844 } else {
845 Sleep(50);
850 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());