2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
33 #include <wine/test.h>
35 static HMODULE hmoduleRichEdit;
37 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
39 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
40 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
41 hmoduleRichEdit, NULL);
42 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
46 static HWND new_richedit(HWND parent) {
47 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
50 static const char haystack[] = "WINEWine wineWine wine WineWine";
63 struct find_s find_tests[] = {
64 /* Find in empty text */
65 {0, -1, "foo", FR_DOWN, -1, 0},
66 {0, -1, "foo", 0, -1, 0},
67 {0, -1, "", FR_DOWN, -1, 0},
68 {20, 5, "foo", FR_DOWN, -1, 0},
69 {5, 20, "foo", FR_DOWN, -1, 0}
72 struct find_s find_tests2[] = {
74 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
75 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
77 /* Subsequent finds */
78 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
79 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
80 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
81 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
84 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
85 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
86 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
88 /* Case-insensitive */
89 {1, 31, "wInE", FR_DOWN, 4, 0},
90 {1, 31, "Wine", FR_DOWN, 4, 0},
92 /* High-to-low ranges */
93 {20, 5, "Wine", FR_DOWN, -1, 0},
94 {2, 1, "Wine", FR_DOWN, -1, 0},
95 {30, 29, "Wine", FR_DOWN, -1, 0},
96 {20, 5, "Wine", 0, 13, 0},
99 {5, 10, "", FR_DOWN, -1, 0},
100 {10, 5, "", FR_DOWN, -1, 0},
101 {0, -1, "", FR_DOWN, -1, 0},
102 {10, 5, "", 0, -1, 0},
104 /* Whole-word search */
105 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
106 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
107 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
108 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
109 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
110 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
111 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
114 {5, 200, "XXX", FR_DOWN, -1, 0},
115 {-20, 20, "Wine", FR_DOWN, -1, 0},
116 {-20, 20, "Wine", FR_DOWN, -1, 0},
117 {-15, -20, "Wine", FR_DOWN, -1, 0},
118 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
120 /* Check the case noted in bug 4479 where matches at end aren't recognized */
121 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
122 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
123 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
124 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
125 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
127 /* The backwards case of bug 4479; bounds look right
128 * Fails because backward find is wrong */
129 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
130 {0, 20, "WINE", FR_MATCHCASE, -1, 0}
133 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
136 memset(&ft, 0, sizeof(ft));
137 ft.chrg.cpMin = f->start;
138 ft.chrg.cpMax = f->end;
139 ft.lpstrText = f->needle;
140 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
141 ok(findloc == f->expected_loc,
142 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
143 name, id, f->needle, f->start, f->end, f->flags, findloc);
146 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
150 memset(&ft, 0, sizeof(ft));
151 ft.chrg.cpMin = f->start;
152 ft.chrg.cpMax = f->end;
153 ft.lpstrText = f->needle;
154 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
155 ok(findloc == f->expected_loc,
156 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
157 name, id, f->needle, f->start, f->end, f->flags, findloc);
158 ok(ft.chrgText.cpMin == f->expected_loc,
159 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
160 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
161 ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
162 : f->expected_loc + strlen(f->needle)),
163 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n",
164 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
167 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
172 for (i = 0; i < num_tests; i++) {
173 if (find[i]._todo_wine) {
175 check_EM_FINDTEXT(hwnd, name, &find[i], i);
176 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
179 check_EM_FINDTEXT(hwnd, name, &find[i], i);
180 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
185 static void test_EM_FINDTEXT(void)
187 HWND hwndRichEdit = new_richedit(NULL);
189 /* Empty rich edit control */
190 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
191 sizeof(find_tests)/sizeof(struct find_s));
193 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
196 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
197 sizeof(find_tests2)/sizeof(struct find_s));
199 DestroyWindow(hwndRichEdit);
202 static const struct getline_s {
207 {0, 10, "foo bar\r"},
212 /* Buffer smaller than line length */
218 static void test_EM_GETLINE(void)
221 HWND hwndRichEdit = new_richedit(NULL);
222 static const int nBuf = 1024;
223 char dest[1024], origdest[1024];
224 const char text[] = "foo bar\n"
228 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
230 memset(origdest, 0xBB, nBuf);
231 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
234 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
235 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
236 memset(dest, 0xBB, nBuf);
237 *(WORD *) dest = gl[i].buffer_len;
239 /* EM_GETLINE appends a "\r\0" to the end of the line
240 * nCopied counts up to and including the '\r' */
241 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
242 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
244 /* two special cases since a parameter is passed via dest */
245 if (gl[i].buffer_len == 0)
246 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
248 else if (gl[i].buffer_len == 1)
249 ok(dest[0] == gl[i].text[0] && !dest[1] &&
250 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
253 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
254 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
255 ok(!strncmp(dest + expected_bytes_written, origdest
256 + expected_bytes_written, nBuf - expected_bytes_written),
257 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
261 DestroyWindow(hwndRichEdit);
264 static int get_scroll_pos_y(HWND hwnd)
267 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
268 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
272 static void move_cursor(HWND hwnd, long charindex)
275 cr.cpMax = charindex;
276 cr.cpMin = charindex;
277 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
280 static void line_scroll(HWND hwnd, int amount)
282 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
285 static void test_EM_SCROLLCARET(void)
288 HWND hwndRichEdit = new_richedit(NULL);
289 const char text[] = "aa\n"
290 "this is a long line of text that should be longer than the "
299 /* Can't verify this */
300 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
302 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
304 /* Caret above visible window */
305 line_scroll(hwndRichEdit, 3);
306 prevY = get_scroll_pos_y(hwndRichEdit);
307 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
308 curY = get_scroll_pos_y(hwndRichEdit);
309 ok(prevY != curY, "%d == %d\n", prevY, curY);
311 /* Caret below visible window */
312 move_cursor(hwndRichEdit, sizeof(text) - 1);
313 line_scroll(hwndRichEdit, -3);
314 prevY = get_scroll_pos_y(hwndRichEdit);
315 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
316 curY = get_scroll_pos_y(hwndRichEdit);
317 ok(prevY != curY, "%d == %d\n", prevY, curY);
319 /* Caret in visible window */
320 move_cursor(hwndRichEdit, sizeof(text) - 2);
321 prevY = get_scroll_pos_y(hwndRichEdit);
322 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
323 curY = get_scroll_pos_y(hwndRichEdit);
324 ok(prevY == curY, "%d != %d\n", prevY, curY);
326 /* Caret still in visible window */
327 line_scroll(hwndRichEdit, -1);
328 prevY = get_scroll_pos_y(hwndRichEdit);
329 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
330 curY = get_scroll_pos_y(hwndRichEdit);
331 ok(prevY == curY, "%d != %d\n", prevY, curY);
333 DestroyWindow(hwndRichEdit);
336 static void test_EM_SETCHARFORMAT(void)
338 HWND hwndRichEdit = new_richedit(NULL);
342 /* Invalid flags, CHARFORMAT2 structure blanked out */
343 memset(&cf2, 0, sizeof(cf2));
344 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
346 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
348 /* A valid flag, CHARFORMAT2 structure blanked out */
349 memset(&cf2, 0, sizeof(cf2));
350 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
352 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
354 /* A valid flag, CHARFORMAT2 structure blanked out */
355 memset(&cf2, 0, sizeof(cf2));
356 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
358 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
360 /* A valid flag, CHARFORMAT2 structure blanked out */
361 memset(&cf2, 0, sizeof(cf2));
362 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
364 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
366 /* A valid flag, CHARFORMAT2 structure blanked out */
367 memset(&cf2, 0, sizeof(cf2));
368 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
370 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
372 /* Invalid flags, CHARFORMAT2 structure minimally filled */
373 memset(&cf2, 0, sizeof(cf2));
374 cf2.cbSize = sizeof(CHARFORMAT2);
375 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
377 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
379 /* A valid flag, CHARFORMAT2 structure minimally filled */
380 memset(&cf2, 0, sizeof(cf2));
381 cf2.cbSize = sizeof(CHARFORMAT2);
382 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
384 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
386 /* A valid flag, CHARFORMAT2 structure minimally filled */
387 memset(&cf2, 0, sizeof(cf2));
388 cf2.cbSize = sizeof(CHARFORMAT2);
389 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
391 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
393 /* A valid flag, CHARFORMAT2 structure minimally filled */
394 memset(&cf2, 0, sizeof(cf2));
395 cf2.cbSize = sizeof(CHARFORMAT2);
396 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
398 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
400 /* A valid flag, CHARFORMAT2 structure minimally filled */
401 memset(&cf2, 0, sizeof(cf2));
402 cf2.cbSize = sizeof(CHARFORMAT2);
403 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
405 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
407 DestroyWindow(hwndRichEdit);
410 static void test_EM_SETTEXTMODE(void)
412 HWND hwndRichEdit = new_richedit(NULL);
413 CHARFORMAT2 cf2, cf2test;
417 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
418 /*Insert text into the control*/
420 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
422 /*Attempt to change the control to plain text mode*/
423 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
424 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
426 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
427 If rich text is pasted, it should have the same formatting as the rest
428 of the text in the control*/
431 *NOTE: If the default text was already italicized, the test will simply
432 reverse; in other words, it will copy a regular "wine" into a plain
433 text window that uses an italicized format*/
434 cf2.cbSize = sizeof(CHARFORMAT2);
435 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
438 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
439 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
441 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
442 however, SCF_ALL has been implemented*/
443 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
444 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
445 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
447 /*Select the string "wine"*/
450 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
452 /*Copy the italicized "wine" to the clipboard*/
453 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
455 /*Reset the formatting to default*/
456 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
457 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
458 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
460 /*Clear the text in the control*/
461 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
463 /*Switch to Plain Text Mode*/
464 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
465 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
467 /*Input "wine" again in normal format*/
468 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
470 /*Paste the italicized "wine" into the control*/
471 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
473 /*Select a character from the first "wine" string*/
476 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
478 /*Retrieve its formatting*/
479 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
482 /*Select a character from the second "wine" string*/
485 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
487 /*Retrieve its formatting*/
488 cf2test.cbSize = sizeof(CHARFORMAT2);
489 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
492 /*Compare the two formattings*/
493 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
494 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
495 cf2.dwEffects, cf2test.dwEffects);
496 /*Test TM_RICHTEXT by: switching back to Rich Text mode
497 printing "wine" in the current format(normal)
498 pasting "wine" from the clipboard(italicized)
499 comparing the two formats(should differ)*/
501 /*Attempt to switch with text in control*/
502 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
503 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
506 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
508 /*Switch into Rich Text mode*/
509 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
510 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
512 /*Print "wine" in normal formatting into the control*/
513 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
515 /*Paste italicized "wine" into the control*/
516 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
518 /*Select text from the first "wine" string*/
521 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
523 /*Retrieve its formatting*/
524 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
527 /*Select text from the second "wine" string*/
530 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
532 /*Retrieve its formatting*/
533 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
536 /*Test that the two formattings are not the same*/
537 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
538 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
539 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
541 DestroyWindow(hwndRichEdit);
544 static void test_TM_PLAINTEXT(void)
546 /*Tests plain text properties*/
548 HWND hwndRichEdit = new_richedit(NULL);
549 CHARFORMAT2 cf2, cf2test;
553 /*Switch to plain text mode*/
555 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
556 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
558 /*Fill control with text*/
560 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
562 /*Select some text and bold it*/
566 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
567 cf2.cbSize = sizeof(CHARFORMAT2);
568 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
571 cf2.dwMask = CFM_BOLD | cf2.dwMask;
572 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
574 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
575 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
577 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
578 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
580 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
581 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
583 /*Get the formatting of those characters*/
585 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
587 /*Get the formatting of some other characters*/
588 cf2test.cbSize = sizeof(CHARFORMAT2);
591 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
592 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
594 /*Test that they are the same as plain text allows only one formatting*/
596 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
597 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
598 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
600 /*Fill the control with a "wine" string, which when inserted will be bold*/
602 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
604 /*Copy the bolded "wine" string*/
608 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
609 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
611 /*Swap back to rich text*/
613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
614 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
616 /*Set the default formatting to bold italics*/
618 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
619 cf2.dwMask |= CFM_ITALIC;
620 cf2.dwEffects ^= CFE_ITALIC;
621 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
622 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
624 /*Set the text in the control to "wine", which will be bold and italicized*/
626 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
628 /*Paste the plain text "wine" string, which should take the insert
629 formatting, which at the moment is bold italics*/
631 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
633 /*Select the first "wine" string and retrieve its formatting*/
637 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
638 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
640 /*Select the second "wine" string and retrieve its formatting*/
644 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
645 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
647 /*Compare the two formattings. They should be the same.*/
649 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
650 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
651 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
652 DestroyWindow(hwndRichEdit);
655 static void test_WM_GETTEXT(void)
657 HWND hwndRichEdit = new_richedit(NULL);
658 static const char text[] = "Hello. My name is RichEdit!";
659 char buffer[1024] = {0};
662 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
663 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
664 result = strcmp(buffer,text);
666 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
667 DestroyWindow(hwndRichEdit);
670 /* FIXME: need to test unimplemented options and robustly test wparam */
671 static void test_EM_SETOPTIONS(void)
673 HWND hwndRichEdit = new_richedit(NULL);
674 static const char text[] = "Hello. My name is RichEdit!";
675 char buffer[1024] = {0};
677 /* NEGATIVE TESTING - NO OPTIONS SET */
678 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
679 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
681 /* testing no readonly by sending 'a' to the control*/
682 SetFocus(hwndRichEdit);
683 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
684 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
686 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
687 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
689 /* READONLY - sending 'a' to the control */
690 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
691 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
692 SetFocus(hwndRichEdit);
693 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
694 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
695 ok(buffer[0]==text[0],
696 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
698 DestroyWindow(hwndRichEdit);
701 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
703 CHARFORMAT2W text_format;
704 int link_present = 0;
705 text_format.cbSize = sizeof(text_format);
706 SendMessage(hwnd, EM_SETSEL, 0, 1);
707 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
708 link_present = text_format.dwEffects & CFE_LINK;
710 { /* control text is url; should get CFE_LINK */
711 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
715 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
719 static HWND new_static_wnd(HWND parent) {
720 return new_window("Static", 0, parent);
723 static void test_EM_AUTOURLDETECT(void)
730 {"http://www.winehq.org", 1},
731 {"http//winehq.org", 0},
732 {"ww.winehq.org", 0},
733 {"www.winehq.org", 1},
734 {"ftp://192.168.1.1", 1},
735 {"ftp//192.168.1.1", 0},
736 {"mailto:your@email.com", 1},
737 {"prospero:prosperoserver", 1},
739 {"news:newserver", 1},
740 {"wais:waisserver", 1}
745 HWND hwndRichEdit, parent;
747 parent = new_static_wnd(NULL);
748 hwndRichEdit = new_richedit(parent);
749 /* Try and pass EM_AUTOURLDETECT some test wParam values */
750 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
751 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
752 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
753 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
754 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
755 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
756 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
757 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
758 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
759 /* for each url, check the text to see if CFE_LINK effect is present */
760 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
761 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
762 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
763 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
764 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
765 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
766 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
767 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
768 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
770 DestroyWindow(hwndRichEdit);
771 DestroyWindow(parent);
774 static void test_EM_SCROLL(void)
777 int r; /* return value */
778 int expr; /* expected return value */
779 HWND hwndRichEdit = new_richedit(NULL);
780 int y_before, y_after; /* units of lines of text */
782 /* test a richedit box containing a single line of text */
783 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
785 for (i = 0; i < 4; i++) {
786 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
788 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
789 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
790 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
791 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
792 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
793 "(i == %d)\n", y_after, i);
797 * test a richedit box that will scroll. There are two general
798 * cases: the case without any long lines and the case with a long
801 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
803 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
805 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
806 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
807 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
808 "LONG LINE \nb\nc\nd\ne");
809 for (j = 0; j < 12; j++) /* reset scrol position to top */
810 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
812 /* get first visible line */
813 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
814 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
816 /* get new current first visible line */
817 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
819 ok(((r & 0xffffff00) == 0x00010000) &&
820 ((r & 0x000000ff) != 0x00000000),
821 "EM_SCROLL page down didn't scroll by a small positive number of "
822 "lines (r == 0x%08x)\n", r);
823 ok(y_after > y_before, "EM_SCROLL page down not functioning "
824 "(line %d scrolled to line %d\n", y_before, y_after);
828 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
829 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
830 ok(((r & 0xffffff00) == 0x0001ff00),
831 "EM_SCROLL page up didn't scroll by a small negative number of lines "
832 "(r == 0x%08x)\n", r);
833 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
834 "%d scrolled to line %d\n", y_before, y_after);
838 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
840 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
842 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
843 "(r == 0x%08x)\n", r);
844 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
845 "1 line (%d scrolled to %d)\n", y_before, y_after);
849 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
851 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
853 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
854 "(r == 0x%08x)\n", r);
855 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
856 "line (%d scrolled to %d)\n", y_before, y_after);
860 r = SendMessage(hwndRichEdit, EM_SCROLL,
861 SB_LINEUP, 0); /* lineup beyond top */
863 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
866 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
867 ok(y_before == y_after,
868 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
872 r = SendMessage(hwndRichEdit, EM_SCROLL,
873 SB_PAGEUP, 0);/*page up beyond top */
875 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
878 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
879 ok(y_before == y_after,
880 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
882 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
883 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
884 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
885 r = SendMessage(hwndRichEdit, EM_SCROLL,
886 SB_PAGEDOWN, 0); /* page down beyond bot */
887 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
890 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
891 ok(y_before == y_after,
892 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
895 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
896 SendMessage(hwndRichEdit, EM_SCROLL,
897 SB_LINEDOWN, 0); /* line down beyond bot */
898 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
901 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
902 ok(y_before == y_after,
903 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
906 DestroyWindow(hwndRichEdit);
909 static void test_EM_SETUNDOLIMIT(void)
911 /* cases we test for:
912 * default behaviour - limiting at 100 undo's
913 * undo disabled - setting a limit of 0
914 * undo limited - undo limit set to some to some number, like 2
915 * bad input - sending a negative number should default to 100 undo's */
917 HWND hwndRichEdit = new_richedit(NULL);
922 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
925 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
926 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
927 also, multiple pastes don't combine like WM_CHAR would */
928 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
930 /* first case - check the default */
931 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
932 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
933 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
934 for (i=0; i<100; i++) /* Undo 100 of them */
935 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
936 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
937 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
939 /* second case - cannot undo */
940 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
941 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
942 SendMessage(hwndRichEdit,
943 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
944 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
945 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
947 /* third case - set it to an arbitrary number */
948 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
949 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
950 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
951 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
952 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
953 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
954 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
955 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
956 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
957 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
958 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
959 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
960 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
961 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
963 /* fourth case - setting negative numbers should default to 100 undos */
964 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
965 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
967 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
969 DestroyWindow(hwndRichEdit);
972 static void test_ES_PASSWORD(void)
974 /* This isn't hugely testable, so we're just going to run it through its paces */
976 HWND hwndRichEdit = new_richedit(NULL);
979 /* First, check the default of a regular control */
980 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
982 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
984 /* Now, set it to something normal */
985 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
986 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
988 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
990 /* Now, set it to something odd */
991 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
992 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
994 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
995 DestroyWindow(hwndRichEdit);
998 static void test_WM_SETTEXT()
1000 HWND hwndRichEdit = new_richedit(NULL);
1001 const char * TestItem1 = "TestSomeText";
1002 const char * TestItem2 = "TestSomeText\r";
1003 const char * TestItem2_after = "TestSomeText\r\n";
1004 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
1005 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
1006 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
1007 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
1008 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
1009 const char * TestItem5_after = "TestSomeText TestSomeText";
1010 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
1011 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
1012 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
1013 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
1014 char buf[1024] = {0};
1017 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1018 any solitary \r to be converted to \r\n on return. Properly paired
1019 \r\n are not affected. It also shows that the special sequence \r\r\n
1020 gets converted to a single space.
1023 #define TEST_SETTEXT(a, b) \
1024 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
1025 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
1026 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
1027 ok (result == strlen(buf), \
1028 "WM_GETTEXT returned %ld instead of expected %u\n", \
1029 result, strlen(buf)); \
1030 result = strcmp(b, buf); \
1032 "WM_SETTEXT round trip: strcmp = %ld\n", result);
1034 TEST_SETTEXT(TestItem1, TestItem1)
1035 TEST_SETTEXT(TestItem2, TestItem2_after)
1036 TEST_SETTEXT(TestItem3, TestItem3_after)
1037 TEST_SETTEXT(TestItem3_after, TestItem3_after)
1038 TEST_SETTEXT(TestItem4, TestItem4_after)
1039 TEST_SETTEXT(TestItem5, TestItem5_after)
1040 TEST_SETTEXT(TestItem6, TestItem6_after)
1041 TEST_SETTEXT(TestItem7, TestItem7_after)
1044 DestroyWindow(hwndRichEdit);
1047 static void test_EM_SETTEXTEX(void)
1049 HWND hwndRichEdit = new_richedit(NULL);
1052 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1054 'T', 'e', 'x', 't', 0};
1055 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1059 const char * TestItem2_after = "TestSomeText\r\n";
1060 WCHAR TestItem3[] = {'T', 'e', 's', 't',
1063 '\r','\n','\r','\n', 0};
1064 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
1068 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
1072 WCHAR TestItem4[] = {'T', 'e', 's', 't',
1075 '\r','\r','\n','\r',
1077 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
1081 #define MAX_BUF_LEN 1024
1082 WCHAR buf[MAX_BUF_LEN];
1086 setText.codepage = 1200; /* no constant for unicode */
1087 getText.codepage = 1200; /* no constant for unicode */
1088 getText.cb = MAX_BUF_LEN;
1089 getText.flags = GT_DEFAULT;
1090 getText.lpDefaultChar = NULL;
1091 getText.lpUsedDefaultChar = NULL;
1094 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1095 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1096 ok(lstrcmpW(buf, TestItem1) == 0,
1097 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1099 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1100 convert \r to \r\n on return
1102 setText.codepage = 1200; /* no constant for unicode */
1103 getText.codepage = 1200; /* no constant for unicode */
1104 getText.cb = MAX_BUF_LEN;
1105 getText.flags = GT_DEFAULT;
1106 getText.lpDefaultChar = NULL;
1107 getText.lpUsedDefaultChar = NULL;
1109 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1110 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1111 ok(lstrcmpW(buf, TestItem2) == 0,
1112 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1114 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1115 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1116 ok(strcmp((const char *)buf, TestItem2_after) == 0,
1117 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1119 /* Baseline test for just-enough buffer space for string */
1120 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1121 getText.codepage = 1200; /* no constant for unicode */
1122 getText.flags = GT_DEFAULT;
1123 getText.lpDefaultChar = NULL;
1124 getText.lpUsedDefaultChar = NULL;
1125 memset(buf, 0, MAX_BUF_LEN);
1126 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1127 ok(lstrcmpW(buf, TestItem2) == 0,
1128 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1130 /* When there is enough space for one character, but not both, of the CRLF
1131 pair at the end of the string, the CR is not copied at all. That is,
1132 the caller must not see CRLF pairs truncated to CR at the end of the
1135 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
1136 getText.codepage = 1200; /* no constant for unicode */
1137 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
1138 getText.lpDefaultChar = NULL;
1139 getText.lpUsedDefaultChar = NULL;
1140 memset(buf, 0, MAX_BUF_LEN);
1141 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1142 ok(lstrcmpW(buf, TestItem1) == 0,
1143 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1146 /* \r\n pairs get changed into \r */
1147 setText.codepage = 1200; /* no constant for unicode */
1148 getText.codepage = 1200; /* no constant for unicode */
1149 getText.cb = MAX_BUF_LEN;
1150 getText.flags = GT_DEFAULT;
1151 getText.lpDefaultChar = NULL;
1152 getText.lpUsedDefaultChar = NULL;
1154 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
1155 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1156 ok(lstrcmpW(buf, TestItem3_after) == 0,
1157 "EM_SETTEXTEX did not convert properly\n");
1159 /* \n also gets changed to \r */
1160 setText.codepage = 1200; /* no constant for unicode */
1161 getText.codepage = 1200; /* no constant for unicode */
1162 getText.cb = MAX_BUF_LEN;
1163 getText.flags = GT_DEFAULT;
1164 getText.lpDefaultChar = NULL;
1165 getText.lpUsedDefaultChar = NULL;
1167 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
1168 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1169 ok(lstrcmpW(buf, TestItem3_after) == 0,
1170 "EM_SETTEXTEX did not convert properly\n");
1172 /* \r\r\n gets changed into single space */
1173 setText.codepage = 1200; /* no constant for unicode */
1174 getText.codepage = 1200; /* no constant for unicode */
1175 getText.cb = MAX_BUF_LEN;
1176 getText.flags = GT_DEFAULT;
1177 getText.lpDefaultChar = NULL;
1178 getText.lpUsedDefaultChar = NULL;
1180 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
1181 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1182 ok(lstrcmpW(buf, TestItem4_after) == 0,
1183 "EM_SETTEXTEX did not convert properly\n");
1185 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1186 (WPARAM)&setText, (LPARAM) NULL);
1187 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1190 "EM_SETTEXTEX returned %d, instead of 1\n",result);
1191 ok(lstrlenW(buf) == 0,
1192 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1194 /* put some text back */
1196 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1197 /* select some text */
1200 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1201 /* replace current selection */
1202 setText.flags = ST_SELECTION;
1203 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1204 (WPARAM)&setText, (LPARAM) NULL);
1206 "EM_SETTEXTEX with NULL lParam to replace selection"
1207 " with no text should return 0. Got %i\n",
1210 /* put some text back */
1212 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1213 /* select some text */
1216 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1217 /* replace current selection */
1218 setText.flags = ST_SELECTION;
1219 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1220 (WPARAM)&setText, (LPARAM) TestItem1);
1222 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1223 ok(result == lstrlenW(TestItem1),
1224 "EM_SETTEXTEX with NULL lParam to replace selection"
1225 " with no text should return 0. Got %i\n",
1227 ok(lstrlenW(buf) == 22,
1228 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1231 DestroyWindow(hwndRichEdit);
1234 static void test_EM_LIMITTEXT(void)
1238 HWND hwndRichEdit = new_richedit(NULL);
1240 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1241 * about setting the length to -1 for multiline edit controls doesn't happen.
1244 /* Don't check default gettextlimit case. That's done in other tests */
1246 /* Set textlimit to 100 */
1247 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1248 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1250 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1252 /* Set textlimit to 0 */
1253 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1254 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1256 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1258 /* Set textlimit to -1 */
1259 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1260 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1262 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1264 /* Set textlimit to -2 */
1265 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1266 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1268 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1270 DestroyWindow (hwndRichEdit);
1274 static void test_EM_EXLIMITTEXT(void)
1276 int i, selBegin, selEnd, len1, len2;
1278 char text[1024 + 1];
1279 char buffer[1024 + 1];
1280 int textlimit = 0; /* multiple of 100 */
1281 HWND hwndRichEdit = new_richedit(NULL);
1283 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1284 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1287 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1288 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1290 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1293 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1294 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1296 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1298 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1299 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1300 /* default for WParam = 0 */
1301 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1303 textlimit = sizeof(text)-1;
1304 memset(text, 'W', textlimit);
1305 text[sizeof(text)-1] = 0;
1306 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1307 /* maxed out text */
1308 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1310 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1311 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1312 len1 = selEnd - selBegin;
1314 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1315 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1316 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1317 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1318 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1319 len2 = selEnd - selBegin;
1322 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1325 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1326 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1327 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1328 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1329 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1330 len1 = selEnd - selBegin;
1333 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1336 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1337 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1338 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1339 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1340 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1341 len2 = selEnd - selBegin;
1344 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1347 /* set text up to the limit, select all the text, then add a char */
1349 memset(text, 'W', textlimit);
1350 text[textlimit] = 0;
1351 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1352 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1353 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1354 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1355 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1356 result = strcmp(buffer, "A");
1357 ok(0 == result, "got string = \"%s\"\n", buffer);
1359 /* WM_SETTEXT not limited */
1361 memset(text, 'W', textlimit);
1362 text[textlimit] = 0;
1363 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1364 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1365 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1367 ok(10 == i, "expected 10 chars\n");
1368 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1369 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1371 /* try inserting more text at end */
1372 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1373 ok(0 == i, "WM_CHAR wasn't processed\n");
1374 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1376 ok(10 == i, "expected 10 chars, got %i\n", i);
1377 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1378 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1380 /* try inserting text at beginning */
1381 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1382 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1383 ok(0 == i, "WM_CHAR wasn't processed\n");
1384 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1386 ok(10 == i, "expected 10 chars, got %i\n", i);
1387 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1388 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1390 /* WM_CHAR is limited */
1392 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1393 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1394 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1395 ok(0 == i, "WM_CHAR wasn't processed\n");
1396 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1397 ok(0 == i, "WM_CHAR wasn't processed\n");
1398 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1400 ok(1 == i, "expected 1 chars, got %i instead\n", i);
1402 DestroyWindow(hwndRichEdit);
1405 static void test_EM_GETLIMITTEXT(void)
1408 HWND hwndRichEdit = new_richedit(NULL);
1410 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1411 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1413 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1414 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1415 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1417 DestroyWindow(hwndRichEdit);
1420 static void test_WM_SETFONT(void)
1422 /* There is no invalid input or error conditions for this function.
1423 * NULL wParam and lParam just fall back to their default values
1424 * It should be noted that even if you use a gibberish name for your fonts
1425 * here, it will still work because the name is stored. They will display as
1426 * System, but will report their name to be whatever they were created as */
1428 HWND hwndRichEdit = new_richedit(NULL);
1429 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1430 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1431 FF_DONTCARE, "Marlett");
1432 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1433 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1434 FF_DONTCARE, "MS Sans Serif");
1435 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1436 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1437 FF_DONTCARE, "Courier");
1438 LOGFONTA sentLogFont;
1439 CHARFORMAT2A returnedCF2A;
1441 returnedCF2A.cbSize = sizeof(returnedCF2A);
1443 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1444 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1445 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1447 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1448 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1449 "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1450 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1452 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1453 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1454 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1455 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1456 "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1457 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1459 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1460 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1461 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1462 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1463 "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1464 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1466 /* This last test is special since we send in NULL. We clear the variables
1467 * and just compare to "System" instead of the sent in font name. */
1468 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1469 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1470 returnedCF2A.cbSize = sizeof(returnedCF2A);
1472 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1473 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1474 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1475 ok (!strcmp("System",returnedCF2A.szFaceName),
1476 "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1478 DestroyWindow(hwndRichEdit);
1482 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1487 const char** str = (const char**)dwCookie;
1488 int size = strlen(*str);
1489 if(size > 3) /* let's make it peice-meal for fun */
1496 memcpy(pbBuff, *str, *pcb);
1502 static void test_EM_GETMODIFY(void)
1504 HWND hwndRichEdit = new_richedit(NULL);
1507 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1509 'T', 'e', 'x', 't', 0};
1510 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1512 'O', 't', 'h', 'e', 'r',
1513 'T', 'e', 'x', 't', 0};
1514 const char* streamText = "hello world";
1519 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1520 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1521 FF_DONTCARE, "Courier");
1523 setText.codepage = 1200; /* no constant for unicode */
1524 setText.flags = ST_KEEPUNDO;
1527 /* modify flag shouldn't be set when richedit is first created */
1528 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1530 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1532 /* setting modify flag should actually set it */
1533 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1534 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1536 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1538 /* clearing modify flag should actually clear it */
1539 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1540 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1542 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1544 /* setting font doesn't change modify flag */
1545 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1546 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1547 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1549 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1551 /* setting text should set modify flag */
1552 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1553 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1554 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1556 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1558 /* undo previous text doesn't reset modify flag */
1559 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1560 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1562 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1564 /* set text with no flag to keep undo stack should not set modify flag */
1565 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1567 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1568 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1570 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1572 /* WM_SETTEXT doesn't modify */
1573 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1574 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1575 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1577 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1579 /* clear the text */
1580 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1581 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1582 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1584 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1587 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1588 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1589 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1590 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1591 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1593 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1595 /* copy/paste text 1 */
1596 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1597 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1598 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1599 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1600 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1602 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1604 /* copy/paste text 2 */
1605 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1606 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1607 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1608 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1609 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1610 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1612 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1615 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1616 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1617 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1618 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1620 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1623 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1624 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1625 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1626 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1628 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1630 /* set char format */
1631 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1632 cf2.cbSize = sizeof(CHARFORMAT2);
1633 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1635 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1636 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1637 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1638 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1639 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
1640 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1642 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1644 /* set para format */
1645 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1646 pf2.cbSize = sizeof(PARAFORMAT2);
1647 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1649 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1650 pf2.wAlignment = PFA_RIGHT;
1651 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1652 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1654 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1657 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1658 es.dwCookie = (DWORD_PTR)&streamText;
1660 es.pfnCallback = test_EM_GETMODIFY_esCallback;
1661 SendMessage(hwndRichEdit, EM_STREAMIN,
1662 (WPARAM)(SF_TEXT), (LPARAM)&es);
1663 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1665 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1667 DestroyWindow(hwndRichEdit);
1673 long expected_retval;
1674 int expected_getsel_start;
1675 int expected_getsel_end;
1676 int _exsetsel_todo_wine;
1677 int _getsel_todo_wine;
1680 const struct exsetsel_s exsetsel_tests[] = {
1682 {5, 10, 10, 5, 10, 0, 0},
1683 {15, 17, 17, 15, 17, 0, 0},
1684 /* test cpMax > strlen() */
1685 {0, 100, 18, 0, 18, 0, 1},
1686 /* test cpMin == cpMax */
1687 {5, 5, 5, 5, 5, 0, 0},
1688 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1689 {-1, 0, 5, 5, 5, 0, 0},
1690 {-1, 17, 5, 5, 5, 0, 0},
1691 {-1, 18, 5, 5, 5, 0, 0},
1692 /* test cpMin < 0 && cpMax < 0 */
1693 {-1, -1, 17, 17, 17, 0, 0},
1694 {-4, -5, 17, 17, 17, 0, 0},
1695 /* test cMin >=0 && cpMax < 0 (bug 6814) */
1696 {0, -1, 18, 0, 18, 0, 1},
1697 {17, -5, 18, 17, 18, 0, 1},
1698 {18, -3, 17, 17, 17, 0, 0},
1699 /* test if cpMin > cpMax */
1700 {15, 19, 18, 15, 18, 0, 1},
1701 {19, 15, 18, 15, 18, 0, 1}
1704 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1709 cr.cpMin = setsel->min;
1710 cr.cpMax = setsel->max;
1711 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1713 if (setsel->_exsetsel_todo_wine) {
1715 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1718 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1721 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1723 if (setsel->_getsel_todo_wine) {
1725 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);
1728 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);
1732 static void test_EM_EXSETSEL(void)
1734 HWND hwndRichEdit = new_richedit(NULL);
1736 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1738 /* sending some text to the window */
1739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1740 /* 01234567890123456*/
1743 for (i = 0; i < num_tests; i++) {
1744 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1747 DestroyWindow(hwndRichEdit);
1750 static void test_EM_REPLACESEL(void)
1752 HWND hwndRichEdit = new_richedit(NULL);
1753 char buffer[1024] = {0};
1758 /* sending some text to the window */
1759 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1760 /* 01234567890123456*/
1763 /* FIXME add more tests */
1764 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
1765 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
1766 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
1767 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1768 r = strcmp(buffer, "testing");
1769 ok(0 == r, "expected %d, got %d\n", 0, r);
1771 DestroyWindow(hwndRichEdit);
1773 hwndRichEdit = new_richedit(NULL);
1775 /* Test behavior with carriage returns and newlines */
1776 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1777 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
1778 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
1779 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1780 r = strcmp(buffer, "RichEdit1");
1781 ok(0 == r, "expected %d, got %d\n", 0, r);
1783 getText.codepage = CP_ACP;
1784 getText.flags = GT_DEFAULT;
1785 getText.lpDefaultChar = NULL;
1786 getText.lpUsedDefaultChar = NULL;
1787 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1788 ok(strcmp(buffer, "RichEdit1") == 0,
1789 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
1791 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1792 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
1793 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
1794 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1795 r = strcmp(buffer, "RichEdit1\r\n");
1796 ok(0 == r, "expected %d, got %d\n", 0, r);
1798 getText.codepage = CP_ACP;
1799 getText.flags = GT_DEFAULT;
1800 getText.lpDefaultChar = NULL;
1801 getText.lpUsedDefaultChar = NULL;
1802 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1803 ok(strcmp(buffer, "RichEdit1\r") == 0,
1804 "EM_GETTEXTEX returned incorrect string\n");
1806 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
1807 EM_REPLACESEL. The general rule seems to be that Win98's riched20
1808 returns the number of characters *inserted* into the control (after
1809 required conversions), but WinXP's riched20 returns the number of
1810 characters interpreted from the original lParam. Wine's builtin riched20
1811 implements the WinXP behavior.
1813 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1814 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
1815 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
1816 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
1818 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1819 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1820 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
1821 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
1823 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1824 r = strcmp(buffer, "RichEdit1\r\n");
1825 ok(0 == r, "expected %d, got %d\n", 0, r);
1827 getText.codepage = CP_ACP;
1828 getText.flags = GT_DEFAULT;
1829 getText.lpDefaultChar = NULL;
1830 getText.lpUsedDefaultChar = NULL;
1831 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1832 ok(strcmp(buffer, "RichEdit1\r") == 0,
1833 "EM_GETTEXTEX returned incorrect string\n");
1835 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1836 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1837 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
1838 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
1840 /* The following tests show that richedit should handle the special \r\r\n
1841 sequence by turning it into a single space on insertion. However,
1842 EM_REPLACESEL on WinXP returns the number of characters in the original
1846 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1847 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
1848 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
1849 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1850 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1851 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
1852 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
1854 /* Test the actual string */
1856 getText.codepage = CP_ACP;
1857 getText.flags = GT_DEFAULT;
1858 getText.lpDefaultChar = NULL;
1859 getText.lpUsedDefaultChar = NULL;
1860 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1861 ok(strcmp(buffer, "\r\r") == 0,
1862 "EM_GETTEXTEX returned incorrect string\n");
1864 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1865 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
1866 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
1867 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
1868 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1869 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1870 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
1871 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
1873 /* Test the actual string */
1875 getText.codepage = CP_ACP;
1876 getText.flags = GT_DEFAULT;
1877 getText.lpDefaultChar = NULL;
1878 getText.lpUsedDefaultChar = NULL;
1879 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1880 ok(strcmp(buffer, " ") == 0,
1881 "EM_GETTEXTEX returned incorrect string\n");
1883 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1884 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
1885 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
1886 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
1887 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1888 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1889 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
1890 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
1892 /* Test the actual string */
1894 getText.codepage = CP_ACP;
1895 getText.flags = GT_DEFAULT;
1896 getText.lpDefaultChar = NULL;
1897 getText.lpUsedDefaultChar = NULL;
1898 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1899 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
1900 "EM_GETTEXTEX returned incorrect string\n");
1902 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1903 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
1904 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
1905 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
1906 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1907 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1908 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
1909 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
1911 /* Test the actual string */
1913 getText.codepage = CP_ACP;
1914 getText.flags = GT_DEFAULT;
1915 getText.lpDefaultChar = NULL;
1916 getText.lpUsedDefaultChar = NULL;
1917 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1918 ok(strcmp(buffer, " \r") == 0,
1919 "EM_GETTEXTEX returned incorrect string\n");
1921 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1922 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
1923 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
1924 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
1925 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1926 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1927 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
1928 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
1930 /* Test the actual string */
1932 getText.codepage = CP_ACP;
1933 getText.flags = GT_DEFAULT;
1934 getText.lpDefaultChar = NULL;
1935 getText.lpUsedDefaultChar = NULL;
1936 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1937 ok(strcmp(buffer, " \r\r") == 0,
1938 "EM_GETTEXTEX returned incorrect string\n");
1940 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1941 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
1942 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
1943 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
1944 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1945 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1946 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
1947 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
1949 /* Test the actual string */
1951 getText.codepage = CP_ACP;
1952 getText.flags = GT_DEFAULT;
1953 getText.lpDefaultChar = NULL;
1954 getText.lpUsedDefaultChar = NULL;
1955 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1956 ok(strcmp(buffer, "\rX\r\r\r") == 0,
1957 "EM_GETTEXTEX returned incorrect string\n");
1959 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1960 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
1961 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
1962 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1963 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1964 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
1965 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
1967 /* Test the actual string */
1969 getText.codepage = CP_ACP;
1970 getText.flags = GT_DEFAULT;
1971 getText.lpDefaultChar = NULL;
1972 getText.lpUsedDefaultChar = NULL;
1973 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1974 ok(strcmp(buffer, "\r\r") == 0,
1975 "EM_GETTEXTEX returned incorrect string\n");
1977 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1978 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
1979 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
1980 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
1981 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1982 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1983 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
1984 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
1986 /* Test the actual string */
1988 getText.codepage = CP_ACP;
1989 getText.flags = GT_DEFAULT;
1990 getText.lpDefaultChar = NULL;
1991 getText.lpUsedDefaultChar = NULL;
1992 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1993 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
1994 "EM_GETTEXTEX returned incorrect string\n");
1996 DestroyWindow(hwndRichEdit);
1999 static void test_WM_PASTE(void)
2003 char buffer[1024] = {0};
2004 char key_info[][3] =
2006 /* VirtualKey, ScanCode, WM_CHAR code */
2007 {'C', 0x2e, 3}, /* Ctrl-C */
2008 {'X', 0x2d, 24}, /* Ctrl-X */
2009 {'V', 0x2f, 22}, /* Ctrl-V */
2010 {'Z', 0x2c, 26}, /* Ctrl-Z */
2011 {'Y', 0x15, 25}, /* Ctrl-Y */
2013 const char* text1 = "testing paste\r";
2014 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
2015 const char* text1_after = "testing paste\r\n";
2016 const char* text2 = "testing paste\r\rtesting paste";
2017 const char* text2_after = "testing paste\r\n\r\ntesting paste";
2018 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
2019 HWND hwndRichEdit = new_richedit(NULL);
2021 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
2022 messages, probably because it inspects the keyboard state itself.
2023 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
2025 #define SEND_CTRL_KEY(hwnd, k) \
2026 keybd_event(VK_CONTROL, 0x1d, 0, 0);\
2027 keybd_event(k[0], k[1], 0, 0);\
2028 keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
2029 keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
2030 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
2031 TranslateMessage(&msg); \
2032 DispatchMessage(&msg); \
2035 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
2036 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
2037 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
2038 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
2039 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
2041 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
2042 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
2044 SEND_CTRL_C(hwndRichEdit) /* Copy */
2045 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2046 SEND_CTRL_V(hwndRichEdit) /* Paste */
2047 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2048 /* Pasted text should be visible at this step */
2049 result = strcmp(text1_step1, buffer);
2051 "test paste: strcmp = %i\n", result);
2052 SEND_CTRL_Z(hwndRichEdit) /* Undo */
2053 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2054 /* Text should be the same as before (except for \r -> \r\n conversion) */
2055 result = strcmp(text1_after, buffer);
2057 "test paste: strcmp = %i\n", result);
2059 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
2060 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
2061 SEND_CTRL_C(hwndRichEdit) /* Copy */
2062 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
2063 SEND_CTRL_V(hwndRichEdit) /* Paste */
2064 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2065 /* Pasted text should be visible at this step */
2066 result = strcmp(text3, buffer);
2068 "test paste: strcmp = %i\n", result);
2069 SEND_CTRL_Z(hwndRichEdit) /* Undo */
2070 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2071 /* Text should be the same as before (except for \r -> \r\n conversion) */
2072 result = strcmp(text2_after, buffer);
2074 "test paste: strcmp = %i\n", result);
2075 SEND_CTRL_Y(hwndRichEdit) /* Redo */
2076 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2077 /* Text should revert to post-paste state */
2078 result = strcmp(buffer,text3);
2080 "test paste: strcmp = %i\n", result);
2082 DestroyWindow(hwndRichEdit);
2085 static void test_EM_FORMATRANGE(void)
2090 HWND hwndRichEdit = new_richedit(NULL);
2092 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
2094 hdc = GetDC(hwndRichEdit);
2095 ok(hdc != NULL, "Could not get HDC\n");
2097 fr.hdc = fr.hdcTarget = hdc;
2098 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
2099 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
2100 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
2104 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2106 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2109 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2111 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
2117 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2119 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
2122 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2124 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2127 DestroyWindow(hwndRichEdit);
2130 static int nCallbackCount = 0;
2132 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
2135 const char text[] = {'t','e','s','t'};
2137 if (sizeof(text) <= cb)
2139 if ((int)dwCookie != nCallbackCount)
2145 memcpy (pbBuff, text, sizeof(text));
2146 *pcb = sizeof(text);
2153 return 1; /* indicates callback failed */
2156 static void test_EM_StreamIn_Undo(void)
2158 /* The purpose of this test is to determine when a EM_StreamIn should be
2159 * undoable. This is important because WM_PASTE currently uses StreamIn and
2160 * pasting should always be undoable but streaming isn't always.
2163 * StreamIn plain text without SFF_SELECTION.
2164 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
2165 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
2166 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
2167 * Feel free to add tests for other text modes or StreamIn things.
2171 HWND hwndRichEdit = new_richedit(NULL);
2174 char buffer[1024] = {0};
2175 const char randomtext[] = "Some text";
2177 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
2179 /* StreamIn, no SFF_SELECTION */
2180 es.dwCookie = nCallbackCount;
2181 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2182 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2183 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2184 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
2185 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2186 result = strcmp (buffer,"test");
2188 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2190 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2191 ok (result == FALSE,
2192 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
2194 /* StreamIn, SFF_SELECTION, but nothing selected */
2195 es.dwCookie = nCallbackCount;
2196 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2197 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2198 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2199 SendMessage(hwndRichEdit, EM_STREAMIN,
2200 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2201 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2202 result = strcmp (buffer,"testSome text");
2204 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2206 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2208 "EM_STREAMIN with SFF_SELECTION but no selection set "
2209 "should create an undo\n");
2211 /* StreamIn, SFF_SELECTION, with a selection */
2212 es.dwCookie = nCallbackCount;
2213 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2214 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2215 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
2216 SendMessage(hwndRichEdit, EM_STREAMIN,
2217 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2218 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2219 result = strcmp (buffer,"Sometesttext");
2221 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2223 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2225 "EM_STREAMIN with SFF_SELECTION and selection set "
2226 "should create an undo\n");
2230 static BOOL is_em_settextex_supported(HWND hwnd)
2232 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
2233 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
2236 static void test_unicode_conversions(void)
2238 static const WCHAR tW[] = {'t',0};
2239 static const WCHAR teW[] = {'t','e',0};
2240 static const WCHAR textW[] = {'t','e','s','t',0};
2241 static const char textA[] = "test";
2245 int is_win9x, em_settextex_supported, ret;
2247 is_win9x = GetVersion() & 0x80000000;
2249 #define set_textA(hwnd, wm_set_text, txt) \
2251 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
2252 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2253 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2254 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2255 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
2257 #define expect_textA(hwnd, wm_get_text, txt) \
2259 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2260 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2261 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2262 memset(bufA, 0xAA, sizeof(bufA)); \
2263 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2264 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2265 ret = lstrcmpA(bufA, txt); \
2266 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
2269 #define set_textW(hwnd, wm_set_text, txt) \
2271 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
2272 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2273 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2274 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2275 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
2277 #define expect_textW(hwnd, wm_get_text, txt) \
2279 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
2280 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2281 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2282 memset(bufW, 0xAA, sizeof(bufW)); \
2285 assert(wm_get_text == EM_GETTEXTEX); \
2286 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2287 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2291 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2292 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
2294 ret = lstrcmpW(bufW, txt); \
2295 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
2297 #define expect_empty(hwnd, wm_get_text) \
2299 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2300 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
2301 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2302 memset(bufA, 0xAA, sizeof(bufA)); \
2303 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2304 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
2305 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
2308 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2309 0, 0, 200, 60, 0, 0, 0, 0);
2310 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2312 ret = IsWindowUnicode(hwnd);
2314 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
2316 ok(ret, "RichEdit20W should be unicode under NT\n");
2318 /* EM_SETTEXTEX is supported starting from version 3.0 */
2319 em_settextex_supported = is_em_settextex_supported(hwnd);
2320 trace("EM_SETTEXTEX is %ssupported on this platform\n",
2321 em_settextex_supported ? "" : "NOT ");
2323 expect_empty(hwnd, WM_GETTEXT);
2324 expect_empty(hwnd, EM_GETTEXTEX);
2326 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
2327 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2328 expect_textA(hwnd, WM_GETTEXT, "t");
2329 expect_textA(hwnd, EM_GETTEXTEX, "t");
2330 expect_textW(hwnd, EM_GETTEXTEX, tW);
2332 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
2333 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2334 expect_textA(hwnd, WM_GETTEXT, "te");
2335 expect_textA(hwnd, EM_GETTEXTEX, "te");
2336 expect_textW(hwnd, EM_GETTEXTEX, teW);
2338 set_textA(hwnd, WM_SETTEXT, NULL);
2339 expect_empty(hwnd, WM_GETTEXT);
2340 expect_empty(hwnd, EM_GETTEXTEX);
2343 set_textA(hwnd, WM_SETTEXT, textW);
2345 set_textA(hwnd, WM_SETTEXT, textA);
2346 expect_textA(hwnd, WM_GETTEXT, textA);
2347 expect_textA(hwnd, EM_GETTEXTEX, textA);
2348 expect_textW(hwnd, EM_GETTEXTEX, textW);
2350 if (em_settextex_supported)
2352 set_textA(hwnd, EM_SETTEXTEX, textA);
2353 expect_textA(hwnd, WM_GETTEXT, textA);
2354 expect_textA(hwnd, EM_GETTEXTEX, textA);
2355 expect_textW(hwnd, EM_GETTEXTEX, textW);
2360 set_textW(hwnd, WM_SETTEXT, textW);
2361 expect_textW(hwnd, WM_GETTEXT, textW);
2362 expect_textA(hwnd, WM_GETTEXT, textA);
2363 expect_textW(hwnd, EM_GETTEXTEX, textW);
2364 expect_textA(hwnd, EM_GETTEXTEX, textA);
2366 if (em_settextex_supported)
2368 set_textW(hwnd, EM_SETTEXTEX, textW);
2369 expect_textW(hwnd, WM_GETTEXT, textW);
2370 expect_textA(hwnd, WM_GETTEXT, textA);
2371 expect_textW(hwnd, EM_GETTEXTEX, textW);
2372 expect_textA(hwnd, EM_GETTEXTEX, textA);
2375 DestroyWindow(hwnd);
2377 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
2378 0, 0, 200, 60, 0, 0, 0, 0);
2379 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2381 ret = IsWindowUnicode(hwnd);
2382 ok(!ret, "RichEdit20A should NOT be unicode\n");
2384 set_textA(hwnd, WM_SETTEXT, textA);
2385 expect_textA(hwnd, WM_GETTEXT, textA);
2386 expect_textA(hwnd, EM_GETTEXTEX, textA);
2387 expect_textW(hwnd, EM_GETTEXTEX, textW);
2389 if (em_settextex_supported)
2391 set_textA(hwnd, EM_SETTEXTEX, textA);
2392 expect_textA(hwnd, WM_GETTEXT, textA);
2393 expect_textA(hwnd, EM_GETTEXTEX, textA);
2394 expect_textW(hwnd, EM_GETTEXTEX, textW);
2399 set_textW(hwnd, WM_SETTEXT, textW);
2400 expect_textW(hwnd, WM_GETTEXT, textW);
2401 expect_textA(hwnd, WM_GETTEXT, textA);
2402 expect_textW(hwnd, EM_GETTEXTEX, textW);
2403 expect_textA(hwnd, EM_GETTEXTEX, textA);
2405 if (em_settextex_supported)
2407 set_textW(hwnd, EM_SETTEXTEX, textW);
2408 expect_textW(hwnd, WM_GETTEXT, textW);
2409 expect_textA(hwnd, WM_GETTEXT, textA);
2410 expect_textW(hwnd, EM_GETTEXTEX, textW);
2411 expect_textA(hwnd, EM_GETTEXTEX, textA);
2414 DestroyWindow(hwnd);
2417 static void test_WM_CHAR(void)
2421 const char * char_list = "abc\rabc\r";
2422 const char * expected_content_single = "abcabc";
2423 const char * expected_content_multi = "abc\r\nabc\r\n";
2424 char buffer[64] = {0};
2427 /* single-line control must IGNORE carriage returns */
2428 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2429 0, 0, 200, 60, 0, 0, 0, 0);
2430 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2433 while (*p != '\0') {
2434 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2435 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2436 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2437 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2441 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2442 ret = strcmp(buffer, expected_content_single);
2443 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2445 DestroyWindow(hwnd);
2447 /* multi-line control inserts CR normally */
2448 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
2449 0, 0, 200, 60, 0, 0, 0, 0);
2450 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2453 while (*p != '\0') {
2454 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2455 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2456 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2457 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2461 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2462 ret = strcmp(buffer, expected_content_multi);
2463 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2465 DestroyWindow(hwnd);
2468 static void test_EM_GETTEXTLENGTHEX(void)
2471 GETTEXTLENGTHEX gtl;
2473 const char * test_string = "a\nb\n\n\r\n";
2474 const char * test_string_after = "a";
2475 char buffer[64] = {0};
2478 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2479 0, 0, 200, 60, 0, 0, 0, 0);
2480 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2482 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2483 gtl.codepage = CP_ACP;
2484 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2485 ok(ret == 0, "ret %d\n",ret);
2487 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2488 gtl.codepage = CP_ACP;
2489 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2490 ok(ret == 0, "ret %d\n",ret);
2492 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
2494 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2495 gtl.codepage = CP_ACP;
2496 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2497 ok(ret == 1, "ret %d\n",ret);
2499 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2500 gtl.codepage = CP_ACP;
2501 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2502 ok(ret == 1, "ret %d\n",ret);
2504 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2505 ret = strcmp(buffer, test_string_after);
2506 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2508 DestroyWindow(hwnd);
2511 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
2512 0, 0, 200, 60, 0, 0, 0, 0);
2513 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2515 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2516 gtl.codepage = CP_ACP;
2517 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2518 todo_wine ok(ret == 0, "ret %d\n",ret);
2520 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2521 gtl.codepage = CP_ACP;
2522 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2523 ok(ret == 0, "ret %d\n",ret);
2525 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
2527 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2528 gtl.codepage = CP_ACP;
2529 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2530 todo_wine ok(ret == 10, "ret %d\n",ret);
2532 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2533 gtl.codepage = CP_ACP;
2534 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
2535 ok(ret == 6, "ret %d\n",ret);
2537 DestroyWindow(hwnd);
2541 /* globals that parent and child access when checking event masks & notifications */
2542 static HWND eventMaskEditHwnd = 0;
2543 static int queriedEventMask;
2544 static int watchForEventMask = 0;
2546 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
2547 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2549 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
2551 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2553 return DefWindowProcA(hwnd, message, wParam, lParam);
2556 /* test event masks in combination with WM_COMMAND */
2557 static void test_eventMask(void)
2562 const char text[] = "foo bar\n";
2565 /* register class to capture WM_COMMAND */
2567 cls.lpfnWndProc = ParentMsgCheckProcA;
2570 cls.hInstance = GetModuleHandleA(0);
2572 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
2573 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
2574 cls.lpszMenuName = NULL;
2575 cls.lpszClassName = "EventMaskParentClass";
2576 if(!RegisterClassA(&cls)) assert(0);
2578 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
2579 0, 0, 200, 60, NULL, NULL, NULL, NULL);
2580 ok (parent != 0, "Failed to create parent window\n");
2582 eventMaskEditHwnd = new_richedit(parent);
2583 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
2585 eventMask = ENM_CHANGE | ENM_UPDATE;
2586 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
2587 ok(ret == ENM_NONE, "wrong event mask\n");
2588 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2589 ok(ret == eventMask, "failed to set event mask\n");
2591 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
2592 queriedEventMask = 0; /* initialize to something other than we expect */
2593 watchForEventMask = EN_CHANGE;
2594 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
2595 ok(ret == TRUE, "failed to set text\n");
2596 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
2597 notification in response to WM_SETTEXT */
2598 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
2599 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
2604 START_TEST( editor )
2609 /* Must explicitly LoadLibrary(). The test has no references to functions in
2610 * RICHED20.DLL, so the linker doesn't actually link to it. */
2611 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
2612 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
2617 test_EM_SCROLLCARET();
2620 test_EM_SETCHARFORMAT();
2621 test_EM_SETTEXTMODE();
2622 test_TM_PLAINTEXT();
2623 test_EM_SETOPTIONS();
2625 test_EM_AUTOURLDETECT();
2626 test_EM_SETUNDOLIMIT();
2628 test_EM_SETTEXTEX();
2629 test_EM_LIMITTEXT();
2630 test_EM_EXLIMITTEXT();
2631 test_EM_GETLIMITTEXT();
2633 test_EM_GETMODIFY();
2636 test_EM_StreamIn_Undo();
2637 test_EM_FORMATRANGE();
2638 test_unicode_conversions();
2639 test_EM_GETTEXTLENGTHEX();
2640 test_EM_REPLACESEL();
2643 /* Set the environment variable WINETEST_RICHED20 to keep windows
2644 * responsive and open for 30 seconds. This is useful for debugging.
2646 * The message pump uses PeekMessage() to empty the queue and then sleeps for
2647 * 50ms before retrying the queue. */
2648 end = time(NULL) + 30;
2649 if (getenv( "WINETEST_RICHED20" )) {
2650 while (time(NULL) < end) {
2651 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
2652 TranslateMessage(&msg);
2653 DispatchMessage(&msg);
2660 OleFlushClipboard();
2661 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());