riched20: EM_SETCHARFORMAT must return 1 on success, not 0.
[wine/multimedia.git] / dlls / riched20 / tests / editor.c
blob7a092c3b85489d17f81f2de75bb6c5f26de8d3b9
1 /*
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
23 #include <stdarg.h>
24 #include <assert.h>
25 #include <windef.h>
26 #include <winbase.h>
27 #include <wingdi.h>
28 #include <winuser.h>
29 #include <winnls.h>
30 #include <ole2.h>
31 #include <richedit.h>
32 #include <time.h>
33 #include <wine/test.h>
35 static HMODULE hmoduleRichEdit;
37 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
38 HWND hwnd;
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());
43 return hwnd;
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";
51 /* ^0 ^10 ^20 ^30 */
53 struct find_s {
54 int start;
55 int end;
56 const char *needle;
57 int flags;
58 int expected_loc;
59 int _todo_wine;
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[] = {
73 /* No-result find */
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},
83 /* Find backwards */
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},
98 /* Find nothing */
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},
113 /* Bad ranges */
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) {
134 int findloc;
135 FINDTEXT ft;
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,
147 int id) {
148 int findloc;
149 FINDTEXTEX ft;
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,
168 int num_tests)
170 int i;
172 for (i = 0; i < num_tests; i++) {
173 if (find[i]._todo_wine) {
174 todo_wine {
175 check_EM_FINDTEXT(hwnd, name, &find[i], i);
176 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
178 } else {
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);
195 /* Haystack text */
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 {
203 int line;
204 size_t buffer_len;
205 const char *text;
206 } gl[] = {
207 {0, 10, "foo bar\r"},
208 {1, 10, "\r"},
209 {2, 10, "bar\r"},
210 {3, 10, "\r"},
212 /* Buffer smaller than line length */
213 {0, 2, "foo bar\r"},
214 {0, 1, "foo bar\r"},
215 {0, 0, "foo bar\r"}
218 static void test_EM_GETLINE(void)
220 int i;
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"
225 "\n"
226 "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++)
233 int nCopied;
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,
243 expected_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),
247 "buffer_len=0\n");
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");
251 else
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)
266 POINT p = {-1, -1};
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);
269 return p.y;
272 static void move_cursor(HWND hwnd, long charindex)
274 CHARRANGE cr;
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)
287 int prevY, curY;
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 "
291 "control's width\n"
292 "cc\n"
293 "dd\n"
294 "ee\n"
295 "ff\n"
296 "gg\n"
297 "hh\n";
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_SETTEXTMODE(void)
338 HWND hwndRichEdit = new_richedit(NULL);
339 CHARFORMAT2 cf2, cf2test;
340 CHARRANGE cr;
341 int rc = 0;
343 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
344 /*Insert text into the control*/
346 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
348 /*Attempt to change the control to plain text mode*/
349 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
350 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
352 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
353 If rich text is pasted, it should have the same formatting as the rest
354 of the text in the control*/
356 /*Italicize the text
357 *NOTE: If the default text was already italicized, the test will simply
358 reverse; in other words, it will copy a regular "wine" into a plain
359 text window that uses an italicized format*/
360 cf2.cbSize = sizeof(CHARFORMAT2);
361 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
362 (LPARAM) &cf2);
364 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
365 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
367 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
368 however, SCF_ALL has been implemented*/
369 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
370 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
371 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
373 /*Select the string "wine"*/
374 cr.cpMin = 0;
375 cr.cpMax = 4;
376 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
378 /*Copy the italicized "wine" to the clipboard*/
379 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
381 /*Reset the formatting to default*/
382 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
383 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
384 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
386 /*Clear the text in the control*/
387 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
389 /*Switch to Plain Text Mode*/
390 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
391 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
393 /*Input "wine" again in normal format*/
394 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
396 /*Paste the italicized "wine" into the control*/
397 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
399 /*Select a character from the first "wine" string*/
400 cr.cpMin = 2;
401 cr.cpMax = 3;
402 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
404 /*Retrieve its formatting*/
405 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
406 (LPARAM) &cf2);
408 /*Select a character from the second "wine" string*/
409 cr.cpMin = 5;
410 cr.cpMax = 6;
411 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
413 /*Retrieve its formatting*/
414 cf2test.cbSize = sizeof(CHARFORMAT2);
415 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
416 (LPARAM) &cf2test);
418 /*Compare the two formattings*/
419 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
420 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
421 cf2.dwEffects, cf2test.dwEffects);
422 /*Test TM_RICHTEXT by: switching back to Rich Text mode
423 printing "wine" in the current format(normal)
424 pasting "wine" from the clipboard(italicized)
425 comparing the two formats(should differ)*/
427 /*Attempt to switch with text in control*/
428 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
429 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
431 /*Clear control*/
432 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
434 /*Switch into Rich Text mode*/
435 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
436 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
438 /*Print "wine" in normal formatting into the control*/
439 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
441 /*Paste italicized "wine" into the control*/
442 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
444 /*Select text from the first "wine" string*/
445 cr.cpMin = 1;
446 cr.cpMax = 3;
447 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
449 /*Retrieve its formatting*/
450 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
451 (LPARAM) &cf2);
453 /*Select text from the second "wine" string*/
454 cr.cpMin = 6;
455 cr.cpMax = 7;
456 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
458 /*Retrieve its formatting*/
459 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
460 (LPARAM) &cf2test);
462 /*Test that the two formattings are not the same*/
463 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
464 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
465 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
467 DestroyWindow(hwndRichEdit);
470 static void test_TM_PLAINTEXT(void)
472 /*Tests plain text properties*/
474 HWND hwndRichEdit = new_richedit(NULL);
475 CHARFORMAT2 cf2, cf2test;
476 CHARRANGE cr;
477 int rc = 0;
479 /*Switch to plain text mode*/
481 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
482 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
484 /*Fill control with text*/
486 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
488 /*Select some text and bold it*/
490 cr.cpMin = 10;
491 cr.cpMax = 20;
492 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
493 cf2.cbSize = sizeof(CHARFORMAT2);
494 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
495 (LPARAM) &cf2);
497 cf2.dwMask = CFM_BOLD | cf2.dwMask;
498 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
500 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
501 todo_wine {
502 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
505 /*Get the formatting of those characters*/
507 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
509 /*Get the formatting of some other characters*/
510 cf2test.cbSize = sizeof(CHARFORMAT2);
511 cr.cpMin = 21;
512 cr.cpMax = 30;
513 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
514 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
516 /*Test that they are the same as plain text allows only one formatting*/
518 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
519 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
520 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
522 /*Fill the control with a "wine" string, which when inserted will be bold*/
524 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
526 /*Copy the bolded "wine" string*/
528 cr.cpMin = 0;
529 cr.cpMax = 4;
530 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
531 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
533 /*Swap back to rich text*/
535 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
536 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
538 /*Set the default formatting to bold italics*/
540 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
541 cf2.dwMask |= CFM_ITALIC;
542 cf2.dwEffects ^= CFE_ITALIC;
543 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
544 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
546 /*Set the text in the control to "wine", which will be bold and italicized*/
548 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
550 /*Paste the plain text "wine" string, which should take the insert
551 formatting, which at the moment is bold italics*/
553 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
555 /*Select the first "wine" string and retrieve its formatting*/
557 cr.cpMin = 1;
558 cr.cpMax = 3;
559 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
560 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
562 /*Select the second "wine" string and retrieve its formatting*/
564 cr.cpMin = 5;
565 cr.cpMax = 7;
566 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
567 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
569 /*Compare the two formattings. They should be the same.*/
571 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
572 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
573 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
574 DestroyWindow(hwndRichEdit);
577 static void test_WM_GETTEXT(void)
579 HWND hwndRichEdit = new_richedit(NULL);
580 static const char text[] = "Hello. My name is RichEdit!";
581 char buffer[1024] = {0};
582 int result;
584 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
585 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
586 result = strcmp(buffer,text);
587 ok(result == 0,
588 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
589 DestroyWindow(hwndRichEdit);
592 /* FIXME: need to test unimplemented options and robustly test wparam */
593 static void test_EM_SETOPTIONS(void)
595 HWND hwndRichEdit = new_richedit(NULL);
596 static const char text[] = "Hello. My name is RichEdit!";
597 char buffer[1024] = {0};
599 /* NEGATIVE TESTING - NO OPTIONS SET */
600 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
601 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
603 /* testing no readonly by sending 'a' to the control*/
604 SetFocus(hwndRichEdit);
605 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
606 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
607 ok(buffer[0]=='a',
608 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
609 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
611 /* READONLY - sending 'a' to the control */
612 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
613 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
614 SetFocus(hwndRichEdit);
615 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
616 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
617 ok(buffer[0]==text[0],
618 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
620 DestroyWindow(hwndRichEdit);
623 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
625 CHARFORMAT2W text_format;
626 int link_present = 0;
627 text_format.cbSize = sizeof(text_format);
628 SendMessage(hwnd, EM_SETSEL, 0, 0);
629 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
630 link_present = text_format.dwEffects & CFE_LINK;
631 if (is_url)
632 { /* control text is url; should get CFE_LINK */
633 ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
635 else
637 ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
641 static HWND new_static_wnd(HWND parent) {
642 return new_window("Static", 0, parent);
645 static void test_EM_AUTOURLDETECT(void)
647 struct urls_s {
648 const char *text;
649 int is_url;
650 } urls[12] = {
651 {"winehq.org", 0},
652 {"http://www.winehq.org", 1},
653 {"http//winehq.org", 0},
654 {"ww.winehq.org", 0},
655 {"www.winehq.org", 1},
656 {"ftp://192.168.1.1", 1},
657 {"ftp//192.168.1.1", 0},
658 {"mailto:your@email.com", 1},
659 {"prospero:prosperoserver", 1},
660 {"telnet:test", 1},
661 {"news:newserver", 1},
662 {"wais:waisserver", 1}
665 int i;
666 int urlRet=-1;
667 HWND hwndRichEdit, parent;
669 parent = new_static_wnd(NULL);
670 hwndRichEdit = new_richedit(parent);
671 /* Try and pass EM_AUTOURLDETECT some test wParam values */
672 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
673 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
674 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
675 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
676 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
677 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
678 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
679 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
680 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
681 /* for each url, check the text to see if CFE_LINK effect is present */
682 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
683 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
684 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
685 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
686 check_CFE_LINK_rcvd(hwndRichEdit, 0);
687 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
688 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
689 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
690 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
692 DestroyWindow(hwndRichEdit);
693 DestroyWindow(parent);
696 static void test_EM_SCROLL(void)
698 int i, j;
699 int r; /* return value */
700 int expr; /* expected return value */
701 HWND hwndRichEdit = new_richedit(NULL);
702 int y_before, y_after; /* units of lines of text */
704 /* test a richedit box containing a single line of text */
705 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
706 expr = 0x00010000;
707 for (i = 0; i < 4; i++) {
708 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
710 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
711 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
712 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
713 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
714 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
715 "(i == %d)\n", y_after, i);
719 * test a richedit box that will scroll. There are two general
720 * cases: the case without any long lines and the case with a long
721 * line.
723 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
724 if (i == 0)
725 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
726 else
727 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
728 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
729 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
730 "LONG LINE \nb\nc\nd\ne");
731 for (j = 0; j < 12; j++) /* reset scrol position to top */
732 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
734 /* get first visible line */
735 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
736 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
738 /* get new current first visible line */
739 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
741 ok(((r & 0xffffff00) == 0x00010000) &&
742 ((r & 0x000000ff) != 0x00000000),
743 "EM_SCROLL page down didn't scroll by a small positive number of "
744 "lines (r == 0x%08x)\n", r);
745 ok(y_after > y_before, "EM_SCROLL page down not functioning "
746 "(line %d scrolled to line %d\n", y_before, y_after);
748 y_before = y_after;
750 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
751 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
752 ok(((r & 0xffffff00) == 0x0001ff00),
753 "EM_SCROLL page up didn't scroll by a small negative number of lines "
754 "(r == 0x%08x)\n", r);
755 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
756 "%d scrolled to line %d\n", y_before, y_after);
758 y_before = y_after;
760 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
762 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
764 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
765 "(r == 0x%08x)\n", r);
766 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
767 "1 line (%d scrolled to %d)\n", y_before, y_after);
769 y_before = y_after;
771 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
773 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
775 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
776 "(r == 0x%08x)\n", r);
777 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
778 "line (%d scrolled to %d)\n", y_before, y_after);
780 y_before = y_after;
782 r = SendMessage(hwndRichEdit, EM_SCROLL,
783 SB_LINEUP, 0); /* lineup beyond top */
785 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
787 ok(r == 0x00010000,
788 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
789 ok(y_before == y_after,
790 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
792 y_before = y_after;
794 r = SendMessage(hwndRichEdit, EM_SCROLL,
795 SB_PAGEUP, 0);/*page up beyond top */
797 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
799 ok(r == 0x00010000,
800 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
801 ok(y_before == y_after,
802 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
804 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
805 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
806 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
807 r = SendMessage(hwndRichEdit, EM_SCROLL,
808 SB_PAGEDOWN, 0); /* page down beyond bot */
809 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
811 ok(r == 0x00010000,
812 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
813 ok(y_before == y_after,
814 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
815 y_before, y_after);
817 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
818 SendMessage(hwndRichEdit, EM_SCROLL,
819 SB_LINEDOWN, 0); /* line down beyond bot */
820 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
822 ok(r == 0x00010000,
823 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
824 ok(y_before == y_after,
825 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
826 y_before, y_after);
828 DestroyWindow(hwndRichEdit);
831 static void test_EM_SETUNDOLIMIT(void)
833 /* cases we test for:
834 * default behaviour - limiting at 100 undo's
835 * undo disabled - setting a limit of 0
836 * undo limited - undo limit set to some to some number, like 2
837 * bad input - sending a negative number should default to 100 undo's */
839 HWND hwndRichEdit = new_richedit(NULL);
840 CHARRANGE cr;
841 int i;
842 int result;
844 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
845 cr.cpMin = 0;
846 cr.cpMax = 1;
847 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
848 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
849 also, multiple pastes don't combine like WM_CHAR would */
850 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
852 /* first case - check the default */
853 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
854 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
855 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
856 for (i=0; i<100; i++) /* Undo 100 of them */
857 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
858 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
859 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
861 /* second case - cannot undo */
862 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
863 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
864 SendMessage(hwndRichEdit,
865 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
866 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
867 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
869 /* third case - set it to an arbitrary number */
870 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
871 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
872 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
873 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
874 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
875 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
876 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
877 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
878 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
879 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
880 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
881 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
882 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
883 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
885 /* fourth case - setting negative numbers should default to 100 undos */
886 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
887 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
888 ok (result == 100,
889 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
891 DestroyWindow(hwndRichEdit);
894 static void test_ES_PASSWORD(void)
896 /* This isn't hugely testable, so we're just going to run it through its paces */
898 HWND hwndRichEdit = new_richedit(NULL);
899 WCHAR result;
901 /* First, check the default of a regular control */
902 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
903 ok (result == 0,
904 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
906 /* Now, set it to something normal */
907 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
908 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
909 ok (result == 120,
910 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
912 /* Now, set it to something odd */
913 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
914 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
915 ok (result == 1234,
916 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
917 DestroyWindow(hwndRichEdit);
920 static void test_WM_SETTEXT()
922 HWND hwndRichEdit = new_richedit(NULL);
923 const char * TestItem1 = "TestSomeText";
924 const char * TestItem2 = "TestSomeText\r";
925 const char * TestItem2_after = "TestSomeText\r\n";
926 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
927 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
928 char buf[1024] = {0};
929 LRESULT result;
931 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
932 any solitary \r to be converted to \r\n on return. Properly paired
933 \r\n are not affected.
936 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
937 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
938 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
939 ok (result == strlen(buf),
940 "WM_GETTEXT returned %ld instead of expected %u\n",
941 result, strlen(buf));
942 result = strcmp(TestItem1, buf);
943 ok(result == 0,
944 "WM_SETTEXT round trip: strcmp = %ld\n", result);
946 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
947 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
948 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
949 ok (result == strlen(buf),
950 "WM_GETTEXT returned %ld instead of expected %u\n",
951 result, strlen(buf));
952 result = strcmp(TestItem2_after, buf);
953 ok(result == 0,
954 "WM_SETTEXT round trip: strcmp = %ld\n", result);
956 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
957 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
958 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
959 ok (result == strlen(buf),
960 "WM_GETTEXT returned %ld instead of expected %u\n",
961 result, strlen(buf));
962 result = strcmp(TestItem3_after, buf);
963 ok(result == 0,
964 "WM_SETTEXT round trip: strcmp = %ld\n", result);
966 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3_after);
967 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
968 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
969 ok (result == strlen(buf),
970 "WM_GETTEXT returned %ld instead of expected %u\n",
971 result, strlen(buf));
972 result = strcmp(TestItem3_after, buf);
973 ok(result == 0,
974 "WM_SETTEXT round trip: strcmp = %ld\n", result);
976 DestroyWindow(hwndRichEdit);
979 static void test_EM_SETTEXTEX(void)
981 HWND hwndRichEdit = new_richedit(NULL);
982 SETTEXTEX setText;
983 GETTEXTEX getText;
984 WCHAR TestItem1[] = {'T', 'e', 's', 't',
985 'S', 'o', 'm', 'e',
986 'T', 'e', 'x', 't', 0};
987 WCHAR TestItem2[] = {'T', 'e', 's', 't',
988 'S', 'o', 'm', 'e',
989 'T', 'e', 'x', 't',
990 '\r', 0};
991 const char * TestItem2_after = "TestSomeText\r\n";
992 #define MAX_BUF_LEN 1024
993 WCHAR buf[MAX_BUF_LEN];
994 int result;
995 CHARRANGE cr;
997 setText.codepage = 1200; /* no constant for unicode */
998 getText.codepage = 1200; /* no constant for unicode */
999 getText.cb = MAX_BUF_LEN;
1000 getText.flags = GT_DEFAULT;
1002 setText.flags = 0;
1003 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1004 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1005 ok(lstrcmpW(buf, TestItem1) == 0,
1006 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1008 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1009 convert \r to \r\n on return
1011 setText.codepage = 1200; /* no constant for unicode */
1012 getText.codepage = 1200; /* no constant for unicode */
1013 getText.cb = MAX_BUF_LEN;
1014 getText.flags = GT_DEFAULT;
1016 setText.flags = 0;
1017 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1018 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1019 ok(lstrcmpW(buf, TestItem2) == 0,
1020 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1022 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1023 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1024 ok(strcmp((const char *)buf, TestItem2_after) == 0,
1025 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1027 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1028 (WPARAM)&setText, (LPARAM) NULL);
1029 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1031 ok (result == 1,
1032 "EM_SETTEXTEX returned %d, instead of 1\n",result);
1033 ok(lstrlenW(buf) == 0,
1034 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1036 /* put some text back */
1037 setText.flags = 0;
1038 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1039 /* select some text */
1040 cr.cpMax = 1;
1041 cr.cpMin = 3;
1042 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1043 /* replace current selection */
1044 setText.flags = ST_SELECTION;
1045 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1046 (WPARAM)&setText, (LPARAM) NULL);
1047 ok(result == 0,
1048 "EM_SETTEXTEX with NULL lParam to replace selection"
1049 " with no text should return 0. Got %i\n",
1050 result);
1052 /* put some text back */
1053 setText.flags = 0;
1054 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1055 /* select some text */
1056 cr.cpMax = 1;
1057 cr.cpMin = 3;
1058 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1059 /* replace current selection */
1060 setText.flags = ST_SELECTION;
1061 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1062 (WPARAM)&setText, (LPARAM) TestItem1);
1063 /* get text */
1064 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1065 ok(result == lstrlenW(TestItem1),
1066 "EM_SETTEXTEX with NULL lParam to replace selection"
1067 " with no text should return 0. Got %i\n",
1068 result);
1069 ok(lstrlenW(buf) == 22,
1070 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1071 lstrlenW(buf) );
1073 DestroyWindow(hwndRichEdit);
1076 static void test_EM_LIMITTEXT(void)
1078 int ret;
1080 HWND hwndRichEdit = new_richedit(NULL);
1082 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1083 * about setting the length to -1 for multiline edit controls doesn't happen.
1086 /* Don't check default gettextlimit case. That's done in other tests */
1088 /* Set textlimit to 100 */
1089 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1090 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1091 ok (ret == 100,
1092 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1094 /* Set textlimit to 0 */
1095 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1096 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1097 ok (ret == 65536,
1098 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1100 /* Set textlimit to -1 */
1101 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1102 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1103 ok (ret == -1,
1104 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1106 /* Set textlimit to -2 */
1107 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1108 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1109 ok (ret == -2,
1110 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1112 DestroyWindow (hwndRichEdit);
1116 static void test_EM_EXLIMITTEXT(void)
1118 int i, selBegin, selEnd, len1, len2;
1119 int result;
1120 char text[1024 + 1];
1121 char buffer[1024 + 1];
1122 int textlimit = 0; /* multiple of 100 */
1123 HWND hwndRichEdit = new_richedit(NULL);
1125 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1126 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1128 textlimit = 256000;
1129 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1130 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1131 /* set higher */
1132 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1134 textlimit = 1000;
1135 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1136 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1137 /* set lower */
1138 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1140 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1141 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1142 /* default for WParam = 0 */
1143 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1145 textlimit = sizeof(text)-1;
1146 memset(text, 'W', textlimit);
1147 text[sizeof(text)-1] = 0;
1148 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1149 /* maxed out text */
1150 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1152 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1153 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1154 len1 = selEnd - selBegin;
1156 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1157 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1158 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1159 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1160 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1161 len2 = selEnd - selBegin;
1163 ok(len1 != len2,
1164 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1165 len1,len2,i);
1167 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1168 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1169 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1170 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1171 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1172 len1 = selEnd - selBegin;
1174 ok(len1 != len2,
1175 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1176 len1,len2,i);
1178 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1179 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1180 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1181 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1182 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1183 len2 = selEnd - selBegin;
1185 ok(len1 == len2,
1186 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1187 len1,len2,i);
1189 /* set text up to the limit, select all the text, then add a char */
1190 textlimit = 5;
1191 memset(text, 'W', textlimit);
1192 text[textlimit] = 0;
1193 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1194 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1195 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1196 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1197 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1198 result = strcmp(buffer, "A");
1199 ok(0 == result, "got string = \"%s\"\n", buffer);
1201 /* WM_SETTEXT not limited */
1202 textlimit = 10;
1203 memset(text, 'W', textlimit);
1204 text[textlimit] = 0;
1205 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1206 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1207 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1208 i = strlen(buffer);
1209 ok(10 == i, "expected 10 chars\n");
1210 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1211 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1213 /* try inserting more text at end */
1214 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1215 ok(0 == i, "WM_CHAR wasn't processed");
1216 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1217 i = strlen(buffer);
1218 ok(10 == i, "expected 10 chars, got %i\n", i);
1219 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1220 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1222 /* try inserting text at beginning */
1223 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1224 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1225 ok(0 == i, "WM_CHAR wasn't processed");
1226 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1227 i = strlen(buffer);
1228 ok(10 == i, "expected 10 chars, got %i\n", i);
1229 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1230 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1232 /* WM_CHAR is limited */
1233 textlimit = 1;
1234 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1235 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1236 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1237 ok(0 == i, "WM_CHAR wasn't processed");
1238 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1239 ok(0 == i, "WM_CHAR wasn't processed");
1240 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1241 i = strlen(buffer);
1242 ok(1 == i, "expected 1 chars, got %i instead\n", i);
1244 DestroyWindow(hwndRichEdit);
1247 static void test_EM_GETLIMITTEXT(void)
1249 int i;
1250 HWND hwndRichEdit = new_richedit(NULL);
1252 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1253 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1255 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1256 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1257 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1259 DestroyWindow(hwndRichEdit);
1262 static void test_WM_SETFONT(void)
1264 /* There is no invalid input or error conditions for this function.
1265 * NULL wParam and lParam just fall back to their default values
1266 * It should be noted that even if you use a gibberish name for your fonts
1267 * here, it will still work because the name is stored. They will display as
1268 * System, but will report their name to be whatever they were created as */
1270 HWND hwndRichEdit = new_richedit(NULL);
1271 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1272 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1273 FF_DONTCARE, "Marlett");
1274 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1275 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1276 FF_DONTCARE, "MS Sans Serif");
1277 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1278 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1279 FF_DONTCARE, "Courier");
1280 LOGFONTA sentLogFont;
1281 CHARFORMAT2A returnedCF2A;
1283 returnedCF2A.cbSize = sizeof(returnedCF2A);
1285 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1286 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1287 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1289 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1290 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1291 "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1292 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1294 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1295 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1296 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1297 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1298 "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1299 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1301 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1302 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1303 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1304 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1305 "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1306 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1308 /* This last test is special since we send in NULL. We clear the variables
1309 * and just compare to "System" instead of the sent in font name. */
1310 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1311 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1312 returnedCF2A.cbSize = sizeof(returnedCF2A);
1314 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1315 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1316 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1317 ok (!strcmp("System",returnedCF2A.szFaceName),
1318 "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1320 DestroyWindow(hwndRichEdit);
1324 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1325 LPBYTE pbBuff,
1326 LONG cb,
1327 LONG *pcb)
1329 const char** str = (const char**)dwCookie;
1330 int size = strlen(*str);
1331 if(size > 3) /* let's make it peice-meal for fun */
1332 size = 3;
1333 *pcb = cb;
1334 if (*pcb > size) {
1335 *pcb = size;
1337 if (*pcb > 0) {
1338 memcpy(pbBuff, *str, *pcb);
1339 *str += *pcb;
1341 return 0;
1344 static void test_EM_GETMODIFY(void)
1346 HWND hwndRichEdit = new_richedit(NULL);
1347 LRESULT result;
1348 SETTEXTEX setText;
1349 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1350 'S', 'o', 'm', 'e',
1351 'T', 'e', 'x', 't', 0};
1352 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1353 'S', 'o', 'm', 'e',
1354 'O', 't', 'h', 'e', 'r',
1355 'T', 'e', 'x', 't', 0};
1356 const char* streamText = "hello world";
1357 CHARFORMAT2 cf2;
1358 PARAFORMAT2 pf2;
1359 EDITSTREAM es;
1361 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1362 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1363 FF_DONTCARE, "Courier");
1365 setText.codepage = 1200; /* no constant for unicode */
1366 setText.flags = ST_KEEPUNDO;
1369 /* modify flag shouldn't be set when richedit is first created */
1370 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1371 ok (result == 0,
1372 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1374 /* setting modify flag should actually set it */
1375 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1376 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1377 ok (result != 0,
1378 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1380 /* clearing modify flag should actually clear it */
1381 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1382 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1383 ok (result == 0,
1384 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1386 /* setting font doesn't change modify flag */
1387 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1388 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1389 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1390 ok (result == 0,
1391 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1393 /* setting text should set modify flag */
1394 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1395 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1396 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1397 ok (result != 0,
1398 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1400 /* undo previous text doesn't reset modify flag */
1401 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1402 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1403 ok (result != 0,
1404 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1406 /* set text with no flag to keep undo stack should not set modify flag */
1407 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1408 setText.flags = 0;
1409 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1410 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1411 ok (result == 0,
1412 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1414 /* WM_SETTEXT doesn't modify */
1415 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1416 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1417 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1418 todo_wine {
1419 ok (result == 0,
1420 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1423 /* clear the text */
1424 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1425 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1426 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1427 ok (result == 0,
1428 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1430 /* replace text */
1431 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1432 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1433 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1434 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1435 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1436 ok (result != 0,
1437 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1439 /* copy/paste text 1 */
1440 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1441 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1442 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1443 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1444 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1445 ok (result != 0,
1446 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1448 /* copy/paste text 2 */
1449 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1450 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1451 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1452 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1453 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1454 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1455 ok (result != 0,
1456 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1458 /* press char */
1459 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1460 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1461 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1462 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1463 ok (result != 0,
1464 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1466 /* press del */
1467 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1468 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1469 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1470 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1471 ok (result != 0,
1472 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1474 /* set char format */
1475 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1476 cf2.cbSize = sizeof(CHARFORMAT2);
1477 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1478 (LPARAM) &cf2);
1479 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1480 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1481 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1482 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1483 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
1484 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1485 ok (result != 0,
1486 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1488 /* set para format */
1489 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1490 pf2.cbSize = sizeof(PARAFORMAT2);
1491 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1492 (LPARAM) &pf2);
1493 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1494 pf2.wAlignment = PFA_RIGHT;
1495 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1496 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1497 ok (result == 0,
1498 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1500 /* EM_STREAM */
1501 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1502 es.dwCookie = (DWORD_PTR)&streamText;
1503 es.dwError = 0;
1504 es.pfnCallback = test_EM_GETMODIFY_esCallback;
1505 SendMessage(hwndRichEdit, EM_STREAMIN,
1506 (WPARAM)(SF_TEXT), (LPARAM)&es);
1507 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1508 ok (result != 0,
1509 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1511 DestroyWindow(hwndRichEdit);
1514 struct exsetsel_s {
1515 long min;
1516 long max;
1517 long expected_retval;
1518 int expected_getsel_start;
1519 int expected_getsel_end;
1520 int _exsetsel_todo_wine;
1521 int _getsel_todo_wine;
1524 const struct exsetsel_s exsetsel_tests[] = {
1525 /* sanity tests */
1526 {5, 10, 10, 5, 10, 0, 0},
1527 {15, 17, 17, 15, 17, 0, 0},
1528 /* test cpMax > strlen() */
1529 {0, 100, 18, 0, 18, 0, 1},
1530 /* test cpMin == cpMax */
1531 {5, 5, 5, 5, 5, 0, 0},
1532 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1533 {-1, 0, 5, 5, 5, 0, 0},
1534 {-1, 17, 5, 5, 5, 0, 0},
1535 {-1, 18, 5, 5, 5, 0, 0},
1536 /* test cpMin < 0 && cpMax < 0 */
1537 {-1, -1, 17, 17, 17, 0, 0},
1538 {-4, -5, 17, 17, 17, 0, 0},
1539 /* test cMin >=0 && cpMax < 0 (bug 6814) */
1540 {0, -1, 18, 0, 18, 0, 1},
1541 {17, -5, 18, 17, 18, 0, 1},
1542 {18, -3, 17, 17, 17, 0, 0},
1543 /* test if cpMin > cpMax */
1544 {15, 19, 18, 15, 18, 0, 1},
1545 {19, 15, 18, 15, 18, 0, 1}
1548 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1549 CHARRANGE cr;
1550 long result;
1551 int start, end;
1553 cr.cpMin = setsel->min;
1554 cr.cpMax = setsel->max;
1555 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1557 if (setsel->_exsetsel_todo_wine) {
1558 todo_wine {
1559 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1561 } else {
1562 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1565 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1567 if (setsel->_getsel_todo_wine) {
1568 todo_wine {
1569 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);
1571 } else {
1572 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);
1576 static void test_EM_EXSETSEL(void)
1578 HWND hwndRichEdit = new_richedit(NULL);
1579 int i;
1580 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1582 /* sending some text to the window */
1583 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1584 /* 01234567890123456*/
1585 /* 10 */
1587 for (i = 0; i < num_tests; i++) {
1588 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1591 DestroyWindow(hwndRichEdit);
1594 static void test_EM_REPLACESEL(void)
1596 HWND hwndRichEdit = new_richedit(NULL);
1597 char buffer[1024] = {0};
1598 int r;
1600 /* sending some text to the window */
1601 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1602 /* 01234567890123456*/
1603 /* 10 */
1605 /* FIXME add more tests */
1606 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
1607 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
1608 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1609 r = strcmp(buffer, "testing");
1610 ok(0 == r, "expected %d, got %d\n", 0, r);
1612 DestroyWindow(hwndRichEdit);
1615 static void test_WM_PASTE(void)
1617 int result;
1618 char buffer[1024] = {0};
1619 const char* text1 = "testing paste\r";
1620 const char* text1_after = "testing paste\r\n";
1621 const char* text2 = "testing paste\r\rtesting paste";
1622 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
1623 HWND hwndRichEdit = new_richedit(NULL);
1625 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1626 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1627 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1628 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1629 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1630 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1631 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1632 result = strcmp(text1_after, buffer);
1633 ok(result == 0,
1634 "test paste: strcmp = %i\n", result);
1636 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1637 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1638 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1639 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1640 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1641 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1642 SendMessage(hwndRichEdit, WM_CHAR, 25, 0); /* ctrl-y */
1643 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1644 result = strcmp(buffer,text3);
1645 ok(result == 0,
1646 "test paste: strcmp = %i\n", result);
1648 DestroyWindow(hwndRichEdit);
1651 static void test_EM_FORMATRANGE(void)
1653 int r;
1654 FORMATRANGE fr;
1655 HDC hdc;
1656 HWND hwndRichEdit = new_richedit(NULL);
1658 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
1660 hdc = GetDC(hwndRichEdit);
1661 ok(hdc != NULL, "Could not get HDC\n");
1663 fr.hdc = fr.hdcTarget = hdc;
1664 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
1665 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
1666 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
1667 fr.chrg.cpMin = 0;
1668 fr.chrg.cpMax = 20;
1670 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1671 todo_wine {
1672 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1675 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1676 todo_wine {
1677 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
1680 fr.chrg.cpMin = 0;
1681 fr.chrg.cpMax = 10;
1683 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1684 todo_wine {
1685 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
1688 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1689 todo_wine {
1690 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1693 DestroyWindow(hwndRichEdit);
1696 static int nCallbackCount = 0;
1698 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1699 LONG cb, LONG* pcb)
1701 const char text[] = {'t','e','s','t'};
1703 if (sizeof(text) <= cb)
1705 if ((int)dwCookie != nCallbackCount)
1707 *pcb = 0;
1708 return 0;
1711 memcpy (pbBuff, text, sizeof(text));
1712 *pcb = sizeof(text);
1714 nCallbackCount++;
1716 return 0;
1718 else
1719 return 1; /* indicates callback failed */
1722 static void test_EM_StreamIn_Undo(void)
1724 /* The purpose of this test is to determine when a EM_StreamIn should be
1725 * undoable. This is important because WM_PASTE currently uses StreamIn and
1726 * pasting should always be undoable but streaming isn't always.
1728 * cases to test:
1729 * StreamIn plain text without SFF_SELECTION.
1730 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1731 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1732 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1733 * Feel free to add tests for other text modes or StreamIn things.
1737 HWND hwndRichEdit = new_richedit(NULL);
1738 LRESULT result;
1739 EDITSTREAM es;
1740 char buffer[1024] = {0};
1741 const char randomtext[] = "Some text";
1743 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1745 /* StreamIn, no SFF_SELECTION */
1746 es.dwCookie = nCallbackCount;
1747 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1748 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1749 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1750 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1751 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1752 result = strcmp (buffer,"test");
1753 ok (result == 0,
1754 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1756 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1757 ok (result == FALSE,
1758 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1760 /* StreamIn, SFF_SELECTION, but nothing selected */
1761 es.dwCookie = nCallbackCount;
1762 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1763 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1764 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1765 SendMessage(hwndRichEdit, EM_STREAMIN,
1766 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1767 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1768 result = strcmp (buffer,"testSome text");
1769 ok (result == 0,
1770 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1772 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1773 ok (result == TRUE,
1774 "EM_STREAMIN with SFF_SELECTION but no selection set "
1775 "should create an undo\n");
1777 /* StreamIn, SFF_SELECTION, with a selection */
1778 es.dwCookie = nCallbackCount;
1779 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1780 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1781 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1782 SendMessage(hwndRichEdit, EM_STREAMIN,
1783 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1784 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1785 result = strcmp (buffer,"Sometesttext");
1786 ok (result == 0,
1787 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1789 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1790 ok (result == TRUE,
1791 "EM_STREAMIN with SFF_SELECTION and selection set "
1792 "should create an undo\n");
1796 static BOOL is_em_settextex_supported(HWND hwnd)
1798 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
1799 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
1802 static void test_unicode_conversions(void)
1804 static const WCHAR tW[] = {'t',0};
1805 static const WCHAR teW[] = {'t','e',0};
1806 static const WCHAR textW[] = {'t','e','s','t',0};
1807 static const char textA[] = "test";
1808 char bufA[64];
1809 WCHAR bufW[64];
1810 HWND hwnd;
1811 int is_win9x, em_settextex_supported, ret;
1813 is_win9x = GetVersion() & 0x80000000;
1815 #define set_textA(hwnd, wm_set_text, txt) \
1816 do { \
1817 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1818 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1819 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1820 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1821 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1822 } while(0)
1823 #define expect_textA(hwnd, wm_get_text, txt) \
1824 do { \
1825 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1826 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1827 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1828 memset(bufA, 0xAA, sizeof(bufA)); \
1829 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1830 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1831 ret = lstrcmpA(bufA, txt); \
1832 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1833 } while(0)
1835 #define set_textW(hwnd, wm_set_text, txt) \
1836 do { \
1837 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1838 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1839 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1840 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1841 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1842 } while(0)
1843 #define expect_textW(hwnd, wm_get_text, txt) \
1844 do { \
1845 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1846 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1847 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1848 memset(bufW, 0xAA, sizeof(bufW)); \
1849 if (is_win9x) \
1851 assert(wm_get_text == EM_GETTEXTEX); \
1852 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1853 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1855 else \
1857 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1858 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1860 ret = lstrcmpW(bufW, txt); \
1861 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1862 } while(0)
1863 #define expect_empty(hwnd, wm_get_text) \
1864 do { \
1865 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1866 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1867 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1868 memset(bufA, 0xAA, sizeof(bufA)); \
1869 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1870 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1871 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1872 } while(0)
1874 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1875 0, 0, 200, 60, 0, 0, 0, 0);
1876 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1878 ret = IsWindowUnicode(hwnd);
1879 if (is_win9x)
1880 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1881 else
1882 ok(ret, "RichEdit20W should be unicode under NT\n");
1884 /* EM_SETTEXTEX is supported starting from version 3.0 */
1885 em_settextex_supported = is_em_settextex_supported(hwnd);
1886 trace("EM_SETTEXTEX is %ssupported on this platform\n",
1887 em_settextex_supported ? "" : "NOT ");
1889 expect_empty(hwnd, WM_GETTEXT);
1890 expect_empty(hwnd, EM_GETTEXTEX);
1892 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1893 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1894 expect_textA(hwnd, WM_GETTEXT, "t");
1895 expect_textA(hwnd, EM_GETTEXTEX, "t");
1896 expect_textW(hwnd, EM_GETTEXTEX, tW);
1898 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1899 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1900 expect_textA(hwnd, WM_GETTEXT, "te");
1901 expect_textA(hwnd, EM_GETTEXTEX, "te");
1902 expect_textW(hwnd, EM_GETTEXTEX, teW);
1904 set_textA(hwnd, WM_SETTEXT, NULL);
1905 expect_empty(hwnd, WM_GETTEXT);
1906 expect_empty(hwnd, EM_GETTEXTEX);
1908 if (is_win9x)
1909 set_textA(hwnd, WM_SETTEXT, textW);
1910 else
1911 set_textA(hwnd, WM_SETTEXT, textA);
1912 expect_textA(hwnd, WM_GETTEXT, textA);
1913 expect_textA(hwnd, EM_GETTEXTEX, textA);
1914 expect_textW(hwnd, EM_GETTEXTEX, textW);
1916 if (em_settextex_supported)
1918 set_textA(hwnd, EM_SETTEXTEX, textA);
1919 expect_textA(hwnd, WM_GETTEXT, textA);
1920 expect_textA(hwnd, EM_GETTEXTEX, textA);
1921 expect_textW(hwnd, EM_GETTEXTEX, textW);
1924 if (!is_win9x)
1926 set_textW(hwnd, WM_SETTEXT, textW);
1927 expect_textW(hwnd, WM_GETTEXT, textW);
1928 expect_textA(hwnd, WM_GETTEXT, textA);
1929 expect_textW(hwnd, EM_GETTEXTEX, textW);
1930 expect_textA(hwnd, EM_GETTEXTEX, textA);
1932 if (em_settextex_supported)
1934 set_textW(hwnd, EM_SETTEXTEX, textW);
1935 expect_textW(hwnd, WM_GETTEXT, textW);
1936 expect_textA(hwnd, WM_GETTEXT, textA);
1937 expect_textW(hwnd, EM_GETTEXTEX, textW);
1938 expect_textA(hwnd, EM_GETTEXTEX, textA);
1941 DestroyWindow(hwnd);
1943 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
1944 0, 0, 200, 60, 0, 0, 0, 0);
1945 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1947 ret = IsWindowUnicode(hwnd);
1948 ok(!ret, "RichEdit20A should NOT be unicode\n");
1950 set_textA(hwnd, WM_SETTEXT, textA);
1951 expect_textA(hwnd, WM_GETTEXT, textA);
1952 expect_textA(hwnd, EM_GETTEXTEX, textA);
1953 expect_textW(hwnd, EM_GETTEXTEX, textW);
1955 if (em_settextex_supported)
1957 set_textA(hwnd, EM_SETTEXTEX, textA);
1958 expect_textA(hwnd, WM_GETTEXT, textA);
1959 expect_textA(hwnd, EM_GETTEXTEX, textA);
1960 expect_textW(hwnd, EM_GETTEXTEX, textW);
1963 if (!is_win9x)
1965 set_textW(hwnd, WM_SETTEXT, textW);
1966 expect_textW(hwnd, WM_GETTEXT, textW);
1967 expect_textA(hwnd, WM_GETTEXT, textA);
1968 expect_textW(hwnd, EM_GETTEXTEX, textW);
1969 expect_textA(hwnd, EM_GETTEXTEX, textA);
1971 if (em_settextex_supported)
1973 set_textW(hwnd, EM_SETTEXTEX, textW);
1974 expect_textW(hwnd, WM_GETTEXT, textW);
1975 expect_textA(hwnd, WM_GETTEXT, textA);
1976 expect_textW(hwnd, EM_GETTEXTEX, textW);
1977 expect_textA(hwnd, EM_GETTEXTEX, textA);
1980 DestroyWindow(hwnd);
1984 static void test_EM_GETTEXTLENGTHEX(void)
1986 HWND hwnd;
1987 GETTEXTLENGTHEX gtl;
1988 int ret;
1990 /* single line */
1991 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1992 0, 0, 200, 60, 0, 0, 0, 0);
1993 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1995 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
1996 gtl.codepage = CP_ACP;
1997 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
1998 ok(ret == 0, "ret %d\n",ret);
2000 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2001 gtl.codepage = CP_ACP;
2002 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2003 ok(ret == 0, "ret %d\n",ret);
2005 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) "a\nb\n\n\r\n");
2007 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2008 gtl.codepage = CP_ACP;
2009 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2010 todo_wine ok(ret == 1, "ret %d\n",ret);
2012 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2013 gtl.codepage = CP_ACP;
2014 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2015 todo_wine ok(ret == 1, "ret %d\n",ret);
2017 DestroyWindow(hwnd);
2019 /* multi line */
2020 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
2021 0, 0, 200, 60, 0, 0, 0, 0);
2022 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2024 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2025 gtl.codepage = CP_ACP;
2026 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2027 todo_wine ok(ret == 0, "ret %d\n",ret);
2029 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2030 gtl.codepage = CP_ACP;
2031 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2032 ok(ret == 0, "ret %d\n",ret);
2034 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) "a\nb\n\n\r\n");
2036 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2037 gtl.codepage = CP_ACP;
2038 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2039 todo_wine ok(ret == 10, "ret %d\n",ret);
2041 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2042 gtl.codepage = CP_ACP;
2043 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2044 ok(ret == 6, "ret %d\n",ret);
2046 DestroyWindow(hwnd);
2050 /* globals that parent and child access when checking event masks & notifications */
2051 static HWND eventMaskEditHwnd = 0;
2052 static int queriedEventMask;
2053 static int watchForEventMask = 0;
2055 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
2056 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2058 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
2060 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2062 return DefWindowProcA(hwnd, message, wParam, lParam);
2065 /* test event masks in combination with WM_COMMAND */
2066 static void test_eventMask(void)
2068 HWND parent;
2069 int ret;
2070 WNDCLASSA cls;
2071 const char text[] = "foo bar\n";
2072 int eventMask;
2074 /* register class to capture WM_COMMAND */
2075 cls.style = 0;
2076 cls.lpfnWndProc = ParentMsgCheckProcA;
2077 cls.cbClsExtra = 0;
2078 cls.cbWndExtra = 0;
2079 cls.hInstance = GetModuleHandleA(0);
2080 cls.hIcon = 0;
2081 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
2082 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
2083 cls.lpszMenuName = NULL;
2084 cls.lpszClassName = "EventMaskParentClass";
2085 if(!RegisterClassA(&cls)) assert(0);
2087 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
2088 0, 0, 200, 60, NULL, NULL, NULL, NULL);
2089 ok (parent != 0, "Failed to create parent window\n");
2091 eventMaskEditHwnd = new_richedit(parent);
2092 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
2094 eventMask = ENM_CHANGE | ENM_UPDATE;
2095 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
2096 ok(ret == ENM_NONE, "wrong event mask\n");
2097 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2098 ok(ret == eventMask, "failed to set event mask\n");
2100 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
2101 queriedEventMask = 0; /* initialize to something other than we expect */
2102 watchForEventMask = EN_CHANGE;
2103 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
2104 ok(ret == TRUE, "failed to set text\n");
2105 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
2106 notification in response to WM_SETTEXT */
2107 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
2108 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
2113 START_TEST( editor )
2115 MSG msg;
2116 time_t end;
2118 /* Must explicitly LoadLibrary(). The test has no references to functions in
2119 * RICHED20.DLL, so the linker doesn't actually link to it. */
2120 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
2121 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
2123 test_EM_FINDTEXT();
2124 test_EM_GETLINE();
2125 test_EM_SCROLLCARET();
2126 test_EM_SCROLL();
2127 test_WM_SETTEXT();
2128 test_EM_SETTEXTMODE();
2129 test_TM_PLAINTEXT();
2130 test_EM_SETOPTIONS();
2131 test_WM_GETTEXT();
2132 test_EM_AUTOURLDETECT();
2133 test_EM_SETUNDOLIMIT();
2134 test_ES_PASSWORD();
2135 test_EM_SETTEXTEX();
2136 test_EM_LIMITTEXT();
2137 test_EM_EXLIMITTEXT();
2138 test_EM_GETLIMITTEXT();
2139 test_WM_SETFONT();
2140 test_EM_GETMODIFY();
2141 test_EM_EXSETSEL();
2142 test_WM_PASTE();
2143 test_EM_StreamIn_Undo();
2144 test_EM_FORMATRANGE();
2145 test_unicode_conversions();
2146 test_EM_GETTEXTLENGTHEX();
2147 test_EM_REPLACESEL();
2148 test_eventMask();
2150 /* Set the environment variable WINETEST_RICHED20 to keep windows
2151 * responsive and open for 30 seconds. This is useful for debugging.
2153 * The message pump uses PeekMessage() to empty the queue and then sleeps for
2154 * 50ms before retrying the queue. */
2155 end = time(NULL) + 30;
2156 if (getenv( "WINETEST_RICHED20" )) {
2157 while (time(NULL) < end) {
2158 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
2159 TranslateMessage(&msg);
2160 DispatchMessage(&msg);
2161 } else {
2162 Sleep(50);
2167 OleFlushClipboard();
2168 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());