push 9ed7f32abbb93ea3a813fd6b1650e5bcfc506606
[wine/hacks.git] / dlls / riched20 / tests / editor.c
blob90899e058eafce45f0fb9b3735793f80594ef4ab
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_SETCHARFORMAT(void)
338 HWND hwndRichEdit = new_richedit(NULL);
339 CHARFORMAT2 cf2;
340 int rc = 0;
342 /* Invalid flags, CHARFORMAT2 structure blanked out */
343 memset(&cf2, 0, sizeof(cf2));
344 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
345 (LPARAM) &cf2);
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,
351 (LPARAM) &cf2);
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,
357 (LPARAM) &cf2);
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,
363 (LPARAM) &cf2);
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,
369 (LPARAM) &cf2);
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,
376 (LPARAM) &cf2);
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,
383 (LPARAM) &cf2);
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,
390 (LPARAM) &cf2);
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,
397 (LPARAM) &cf2);
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,
404 (LPARAM) &cf2);
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;
414 CHARRANGE cr;
415 int rc = 0;
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*/
430 /*Italicize the text
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,
436 (LPARAM) &cf2);
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"*/
448 cr.cpMin = 0;
449 cr.cpMax = 4;
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*/
474 cr.cpMin = 2;
475 cr.cpMax = 3;
476 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
478 /*Retrieve its formatting*/
479 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
480 (LPARAM) &cf2);
482 /*Select a character from the second "wine" string*/
483 cr.cpMin = 5;
484 cr.cpMax = 6;
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,
490 (LPARAM) &cf2test);
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);
505 /*Clear control*/
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*/
519 cr.cpMin = 1;
520 cr.cpMax = 3;
521 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
523 /*Retrieve its formatting*/
524 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
525 (LPARAM) &cf2);
527 /*Select text from the second "wine" string*/
528 cr.cpMin = 6;
529 cr.cpMax = 7;
530 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
532 /*Retrieve its formatting*/
533 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
534 (LPARAM) &cf2test);
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;
550 CHARRANGE cr;
551 int rc = 0;
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*/
564 cr.cpMin = 10;
565 cr.cpMax = 20;
566 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
567 cf2.cbSize = sizeof(CHARFORMAT2);
568 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
569 (LPARAM) &cf2);
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);
589 cr.cpMin = 21;
590 cr.cpMax = 30;
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*/
606 cr.cpMin = 0;
607 cr.cpMax = 4;
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*/
635 cr.cpMin = 1;
636 cr.cpMax = 3;
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*/
642 cr.cpMin = 5;
643 cr.cpMax = 7;
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};
660 int result;
662 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
663 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
664 result = strcmp(buffer,text);
665 ok(result == 0,
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);
685 ok(buffer[0]=='a',
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)
703 CHARFORMAT2W text_format;
704 int link_present = 0;
705 text_format.cbSize = sizeof(text_format);
706 SendMessage(hwnd, EM_SETSEL, 0, 0);
707 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
708 link_present = text_format.dwEffects & CFE_LINK;
709 if (is_url)
710 { /* control text is url; should get CFE_LINK */
711 ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
713 else
715 ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
719 static HWND new_static_wnd(HWND parent) {
720 return new_window("Static", 0, parent);
723 static void test_EM_AUTOURLDETECT(void)
725 struct urls_s {
726 const char *text;
727 int is_url;
728 } urls[12] = {
729 {"winehq.org", 0},
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},
738 {"telnet:test", 1},
739 {"news:newserver", 1},
740 {"wais:waisserver", 1}
743 int i;
744 int urlRet=-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);
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);
770 DestroyWindow(hwndRichEdit);
771 DestroyWindow(parent);
774 static void test_EM_SCROLL(void)
776 int i, j;
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 */
784 expr = 0x00010000;
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
799 * line.
801 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
802 if (i == 0)
803 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
804 else
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);
826 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);
836 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);
847 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);
858 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);
865 ok(r == 0x00010000,
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);
870 y_before = 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);
877 ok(r == 0x00010000,
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);
889 ok(r == 0x00010000,
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",
893 y_before, y_after);
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);
900 ok(r == 0x00010000,
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",
904 y_before, y_after);
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);
918 CHARRANGE cr;
919 int i;
920 int result;
922 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
923 cr.cpMin = 0;
924 cr.cpMax = 1;
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);
966 ok (result == 100,
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);
977 WCHAR result;
979 /* First, check the default of a regular control */
980 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
981 ok (result == 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);
987 ok (result == 120,
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);
993 ok (result == 1234,
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 char buf[1024] = {0};
1007 LRESULT result;
1009 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1010 any solitary \r to be converted to \r\n on return. Properly paired
1011 \r\n are not affected.
1014 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
1015 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
1016 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
1017 ok (result == strlen(buf),
1018 "WM_GETTEXT returned %ld instead of expected %u\n",
1019 result, strlen(buf));
1020 result = strcmp(TestItem1, buf);
1021 ok(result == 0,
1022 "WM_SETTEXT round trip: strcmp = %ld\n", result);
1024 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
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(TestItem2_after, buf);
1031 ok(result == 0,
1032 "WM_SETTEXT round trip: strcmp = %ld\n", result);
1034 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
1035 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
1036 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
1037 ok (result == strlen(buf),
1038 "WM_GETTEXT returned %ld instead of expected %u\n",
1039 result, strlen(buf));
1040 result = strcmp(TestItem3_after, buf);
1041 ok(result == 0,
1042 "WM_SETTEXT round trip: strcmp = %ld\n", result);
1044 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3_after);
1045 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result);
1046 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
1047 ok (result == strlen(buf),
1048 "WM_GETTEXT returned %ld instead of expected %u\n",
1049 result, strlen(buf));
1050 result = strcmp(TestItem3_after, buf);
1051 ok(result == 0,
1052 "WM_SETTEXT round trip: strcmp = %ld\n", result);
1054 DestroyWindow(hwndRichEdit);
1057 static void test_EM_SETTEXTEX(void)
1059 HWND hwndRichEdit = new_richedit(NULL);
1060 SETTEXTEX setText;
1061 GETTEXTEX getText;
1062 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1063 'S', 'o', 'm', 'e',
1064 'T', 'e', 'x', 't', 0};
1065 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1066 'S', 'o', 'm', 'e',
1067 'T', 'e', 'x', 't',
1068 '\r', 0};
1069 const char * TestItem2_after = "TestSomeText\r\n";
1070 #define MAX_BUF_LEN 1024
1071 WCHAR buf[MAX_BUF_LEN];
1072 int result;
1073 CHARRANGE cr;
1075 setText.codepage = 1200; /* no constant for unicode */
1076 getText.codepage = 1200; /* no constant for unicode */
1077 getText.cb = MAX_BUF_LEN;
1078 getText.flags = GT_DEFAULT;
1080 setText.flags = 0;
1081 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1082 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1083 ok(lstrcmpW(buf, TestItem1) == 0,
1084 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1086 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1087 convert \r to \r\n on return
1089 setText.codepage = 1200; /* no constant for unicode */
1090 getText.codepage = 1200; /* no constant for unicode */
1091 getText.cb = MAX_BUF_LEN;
1092 getText.flags = GT_DEFAULT;
1094 setText.flags = 0;
1095 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1096 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1097 ok(lstrcmpW(buf, TestItem2) == 0,
1098 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1100 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1101 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1102 ok(strcmp((const char *)buf, TestItem2_after) == 0,
1103 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1105 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1106 (WPARAM)&setText, (LPARAM) NULL);
1107 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1109 ok (result == 1,
1110 "EM_SETTEXTEX returned %d, instead of 1\n",result);
1111 ok(lstrlenW(buf) == 0,
1112 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1114 /* put some text back */
1115 setText.flags = 0;
1116 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1117 /* select some text */
1118 cr.cpMax = 1;
1119 cr.cpMin = 3;
1120 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1121 /* replace current selection */
1122 setText.flags = ST_SELECTION;
1123 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1124 (WPARAM)&setText, (LPARAM) NULL);
1125 ok(result == 0,
1126 "EM_SETTEXTEX with NULL lParam to replace selection"
1127 " with no text should return 0. Got %i\n",
1128 result);
1130 /* put some text back */
1131 setText.flags = 0;
1132 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1133 /* select some text */
1134 cr.cpMax = 1;
1135 cr.cpMin = 3;
1136 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1137 /* replace current selection */
1138 setText.flags = ST_SELECTION;
1139 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1140 (WPARAM)&setText, (LPARAM) TestItem1);
1141 /* get text */
1142 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1143 ok(result == lstrlenW(TestItem1),
1144 "EM_SETTEXTEX with NULL lParam to replace selection"
1145 " with no text should return 0. Got %i\n",
1146 result);
1147 ok(lstrlenW(buf) == 22,
1148 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1149 lstrlenW(buf) );
1151 DestroyWindow(hwndRichEdit);
1154 static void test_EM_LIMITTEXT(void)
1156 int ret;
1158 HWND hwndRichEdit = new_richedit(NULL);
1160 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1161 * about setting the length to -1 for multiline edit controls doesn't happen.
1164 /* Don't check default gettextlimit case. That's done in other tests */
1166 /* Set textlimit to 100 */
1167 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1168 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1169 ok (ret == 100,
1170 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1172 /* Set textlimit to 0 */
1173 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1174 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1175 ok (ret == 65536,
1176 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1178 /* Set textlimit to -1 */
1179 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1180 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1181 ok (ret == -1,
1182 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1184 /* Set textlimit to -2 */
1185 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1186 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1187 ok (ret == -2,
1188 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1190 DestroyWindow (hwndRichEdit);
1194 static void test_EM_EXLIMITTEXT(void)
1196 int i, selBegin, selEnd, len1, len2;
1197 int result;
1198 char text[1024 + 1];
1199 char buffer[1024 + 1];
1200 int textlimit = 0; /* multiple of 100 */
1201 HWND hwndRichEdit = new_richedit(NULL);
1203 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1204 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1206 textlimit = 256000;
1207 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1208 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1209 /* set higher */
1210 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1212 textlimit = 1000;
1213 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1214 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1215 /* set lower */
1216 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1218 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1219 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1220 /* default for WParam = 0 */
1221 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1223 textlimit = sizeof(text)-1;
1224 memset(text, 'W', textlimit);
1225 text[sizeof(text)-1] = 0;
1226 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1227 /* maxed out text */
1228 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1230 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1231 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1232 len1 = selEnd - selBegin;
1234 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1235 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1236 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1237 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1238 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1239 len2 = selEnd - selBegin;
1241 ok(len1 != len2,
1242 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1243 len1,len2,i);
1245 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1246 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1247 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1248 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1249 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1250 len1 = selEnd - selBegin;
1252 ok(len1 != len2,
1253 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1254 len1,len2,i);
1256 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1257 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1258 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1259 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1260 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1261 len2 = selEnd - selBegin;
1263 ok(len1 == len2,
1264 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1265 len1,len2,i);
1267 /* set text up to the limit, select all the text, then add a char */
1268 textlimit = 5;
1269 memset(text, 'W', textlimit);
1270 text[textlimit] = 0;
1271 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1272 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1273 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1274 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1275 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1276 result = strcmp(buffer, "A");
1277 ok(0 == result, "got string = \"%s\"\n", buffer);
1279 /* WM_SETTEXT not limited */
1280 textlimit = 10;
1281 memset(text, 'W', textlimit);
1282 text[textlimit] = 0;
1283 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1284 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1285 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1286 i = strlen(buffer);
1287 ok(10 == i, "expected 10 chars\n");
1288 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1289 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1291 /* try inserting more text at end */
1292 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1293 ok(0 == i, "WM_CHAR wasn't processed\n");
1294 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1295 i = strlen(buffer);
1296 ok(10 == i, "expected 10 chars, got %i\n", i);
1297 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1298 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1300 /* try inserting text at beginning */
1301 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1302 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1303 ok(0 == i, "WM_CHAR wasn't processed\n");
1304 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1305 i = strlen(buffer);
1306 ok(10 == i, "expected 10 chars, got %i\n", i);
1307 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1308 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1310 /* WM_CHAR is limited */
1311 textlimit = 1;
1312 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1313 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1314 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1315 ok(0 == i, "WM_CHAR wasn't processed\n");
1316 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1317 ok(0 == i, "WM_CHAR wasn't processed\n");
1318 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1319 i = strlen(buffer);
1320 ok(1 == i, "expected 1 chars, got %i instead\n", i);
1322 DestroyWindow(hwndRichEdit);
1325 static void test_EM_GETLIMITTEXT(void)
1327 int i;
1328 HWND hwndRichEdit = new_richedit(NULL);
1330 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1331 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1333 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1334 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1335 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1337 DestroyWindow(hwndRichEdit);
1340 static void test_WM_SETFONT(void)
1342 /* There is no invalid input or error conditions for this function.
1343 * NULL wParam and lParam just fall back to their default values
1344 * It should be noted that even if you use a gibberish name for your fonts
1345 * here, it will still work because the name is stored. They will display as
1346 * System, but will report their name to be whatever they were created as */
1348 HWND hwndRichEdit = new_richedit(NULL);
1349 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1350 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1351 FF_DONTCARE, "Marlett");
1352 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1353 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1354 FF_DONTCARE, "MS Sans Serif");
1355 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1356 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1357 FF_DONTCARE, "Courier");
1358 LOGFONTA sentLogFont;
1359 CHARFORMAT2A returnedCF2A;
1361 returnedCF2A.cbSize = sizeof(returnedCF2A);
1363 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1364 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1365 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1367 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1368 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1369 "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1370 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1372 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1373 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1374 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1375 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1376 "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1377 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1379 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1380 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1381 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1382 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1383 "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1384 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1386 /* This last test is special since we send in NULL. We clear the variables
1387 * and just compare to "System" instead of the sent in font name. */
1388 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1389 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1390 returnedCF2A.cbSize = sizeof(returnedCF2A);
1392 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1393 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1394 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1395 ok (!strcmp("System",returnedCF2A.szFaceName),
1396 "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1398 DestroyWindow(hwndRichEdit);
1402 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1403 LPBYTE pbBuff,
1404 LONG cb,
1405 LONG *pcb)
1407 const char** str = (const char**)dwCookie;
1408 int size = strlen(*str);
1409 if(size > 3) /* let's make it peice-meal for fun */
1410 size = 3;
1411 *pcb = cb;
1412 if (*pcb > size) {
1413 *pcb = size;
1415 if (*pcb > 0) {
1416 memcpy(pbBuff, *str, *pcb);
1417 *str += *pcb;
1419 return 0;
1422 static void test_EM_GETMODIFY(void)
1424 HWND hwndRichEdit = new_richedit(NULL);
1425 LRESULT result;
1426 SETTEXTEX setText;
1427 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1428 'S', 'o', 'm', 'e',
1429 'T', 'e', 'x', 't', 0};
1430 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1431 'S', 'o', 'm', 'e',
1432 'O', 't', 'h', 'e', 'r',
1433 'T', 'e', 'x', 't', 0};
1434 const char* streamText = "hello world";
1435 CHARFORMAT2 cf2;
1436 PARAFORMAT2 pf2;
1437 EDITSTREAM es;
1439 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1440 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1441 FF_DONTCARE, "Courier");
1443 setText.codepage = 1200; /* no constant for unicode */
1444 setText.flags = ST_KEEPUNDO;
1447 /* modify flag shouldn't be set when richedit is first created */
1448 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1449 ok (result == 0,
1450 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1452 /* setting modify flag should actually set it */
1453 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1454 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1455 ok (result != 0,
1456 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1458 /* clearing modify flag should actually clear it */
1459 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1460 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1461 ok (result == 0,
1462 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1464 /* setting font doesn't change modify flag */
1465 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1466 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1467 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1468 ok (result == 0,
1469 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1471 /* setting text should set modify flag */
1472 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1473 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1474 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1475 ok (result != 0,
1476 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1478 /* undo previous text doesn't reset modify flag */
1479 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1480 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1481 ok (result != 0,
1482 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1484 /* set text with no flag to keep undo stack should not set modify flag */
1485 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1486 setText.flags = 0;
1487 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1488 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1489 ok (result == 0,
1490 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1492 /* WM_SETTEXT doesn't modify */
1493 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1494 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1495 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1496 todo_wine {
1497 ok (result == 0,
1498 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1501 /* clear the text */
1502 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1503 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1504 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1505 ok (result == 0,
1506 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1508 /* replace text */
1509 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1510 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1511 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1512 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1513 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1514 ok (result != 0,
1515 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1517 /* copy/paste text 1 */
1518 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1519 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1520 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1521 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1522 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1523 ok (result != 0,
1524 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1526 /* copy/paste text 2 */
1527 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1528 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1529 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1530 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1531 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1532 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1533 ok (result != 0,
1534 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1536 /* press char */
1537 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1538 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1539 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1540 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1541 ok (result != 0,
1542 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1544 /* press del */
1545 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1546 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1547 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1548 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1549 ok (result != 0,
1550 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1552 /* set char format */
1553 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1554 cf2.cbSize = sizeof(CHARFORMAT2);
1555 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1556 (LPARAM) &cf2);
1557 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1558 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1559 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1560 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1561 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
1562 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1563 ok (result != 0,
1564 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1566 /* set para format */
1567 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1568 pf2.cbSize = sizeof(PARAFORMAT2);
1569 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1570 (LPARAM) &pf2);
1571 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1572 pf2.wAlignment = PFA_RIGHT;
1573 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1574 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1575 ok (result == 0,
1576 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1578 /* EM_STREAM */
1579 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1580 es.dwCookie = (DWORD_PTR)&streamText;
1581 es.dwError = 0;
1582 es.pfnCallback = test_EM_GETMODIFY_esCallback;
1583 SendMessage(hwndRichEdit, EM_STREAMIN,
1584 (WPARAM)(SF_TEXT), (LPARAM)&es);
1585 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1586 ok (result != 0,
1587 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1589 DestroyWindow(hwndRichEdit);
1592 struct exsetsel_s {
1593 long min;
1594 long max;
1595 long expected_retval;
1596 int expected_getsel_start;
1597 int expected_getsel_end;
1598 int _exsetsel_todo_wine;
1599 int _getsel_todo_wine;
1602 const struct exsetsel_s exsetsel_tests[] = {
1603 /* sanity tests */
1604 {5, 10, 10, 5, 10, 0, 0},
1605 {15, 17, 17, 15, 17, 0, 0},
1606 /* test cpMax > strlen() */
1607 {0, 100, 18, 0, 18, 0, 1},
1608 /* test cpMin == cpMax */
1609 {5, 5, 5, 5, 5, 0, 0},
1610 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1611 {-1, 0, 5, 5, 5, 0, 0},
1612 {-1, 17, 5, 5, 5, 0, 0},
1613 {-1, 18, 5, 5, 5, 0, 0},
1614 /* test cpMin < 0 && cpMax < 0 */
1615 {-1, -1, 17, 17, 17, 0, 0},
1616 {-4, -5, 17, 17, 17, 0, 0},
1617 /* test cMin >=0 && cpMax < 0 (bug 6814) */
1618 {0, -1, 18, 0, 18, 0, 1},
1619 {17, -5, 18, 17, 18, 0, 1},
1620 {18, -3, 17, 17, 17, 0, 0},
1621 /* test if cpMin > cpMax */
1622 {15, 19, 18, 15, 18, 0, 1},
1623 {19, 15, 18, 15, 18, 0, 1}
1626 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1627 CHARRANGE cr;
1628 long result;
1629 int start, end;
1631 cr.cpMin = setsel->min;
1632 cr.cpMax = setsel->max;
1633 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1635 if (setsel->_exsetsel_todo_wine) {
1636 todo_wine {
1637 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1639 } else {
1640 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1643 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1645 if (setsel->_getsel_todo_wine) {
1646 todo_wine {
1647 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);
1649 } else {
1650 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);
1654 static void test_EM_EXSETSEL(void)
1656 HWND hwndRichEdit = new_richedit(NULL);
1657 int i;
1658 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1660 /* sending some text to the window */
1661 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1662 /* 01234567890123456*/
1663 /* 10 */
1665 for (i = 0; i < num_tests; i++) {
1666 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1669 DestroyWindow(hwndRichEdit);
1672 static void test_EM_REPLACESEL(void)
1674 HWND hwndRichEdit = new_richedit(NULL);
1675 char buffer[1024] = {0};
1676 int r;
1678 /* sending some text to the window */
1679 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1680 /* 01234567890123456*/
1681 /* 10 */
1683 /* FIXME add more tests */
1684 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
1685 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
1686 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1687 r = strcmp(buffer, "testing");
1688 ok(0 == r, "expected %d, got %d\n", 0, r);
1690 DestroyWindow(hwndRichEdit);
1693 static void test_WM_PASTE(void)
1695 int result;
1696 char buffer[1024] = {0};
1697 const char* text1 = "testing paste\r";
1698 const char* text1_after = "testing paste\r\n";
1699 const char* text2 = "testing paste\r\rtesting paste";
1700 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
1701 HWND hwndRichEdit = new_richedit(NULL);
1703 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1704 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1705 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1706 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1707 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1708 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1709 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1710 result = strcmp(text1_after, buffer);
1711 ok(result == 0,
1712 "test paste: strcmp = %i\n", result);
1714 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1715 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1716 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1717 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1718 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1719 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1720 SendMessage(hwndRichEdit, WM_CHAR, 25, 0); /* ctrl-y */
1721 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1722 result = strcmp(buffer,text3);
1723 ok(result == 0,
1724 "test paste: strcmp = %i\n", result);
1726 DestroyWindow(hwndRichEdit);
1729 static void test_EM_FORMATRANGE(void)
1731 int r;
1732 FORMATRANGE fr;
1733 HDC hdc;
1734 HWND hwndRichEdit = new_richedit(NULL);
1736 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
1738 hdc = GetDC(hwndRichEdit);
1739 ok(hdc != NULL, "Could not get HDC\n");
1741 fr.hdc = fr.hdcTarget = hdc;
1742 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
1743 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
1744 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
1745 fr.chrg.cpMin = 0;
1746 fr.chrg.cpMax = 20;
1748 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1749 todo_wine {
1750 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1753 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1754 todo_wine {
1755 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
1758 fr.chrg.cpMin = 0;
1759 fr.chrg.cpMax = 10;
1761 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1762 todo_wine {
1763 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
1766 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1767 todo_wine {
1768 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1771 DestroyWindow(hwndRichEdit);
1774 static int nCallbackCount = 0;
1776 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1777 LONG cb, LONG* pcb)
1779 const char text[] = {'t','e','s','t'};
1781 if (sizeof(text) <= cb)
1783 if ((int)dwCookie != nCallbackCount)
1785 *pcb = 0;
1786 return 0;
1789 memcpy (pbBuff, text, sizeof(text));
1790 *pcb = sizeof(text);
1792 nCallbackCount++;
1794 return 0;
1796 else
1797 return 1; /* indicates callback failed */
1800 static void test_EM_StreamIn_Undo(void)
1802 /* The purpose of this test is to determine when a EM_StreamIn should be
1803 * undoable. This is important because WM_PASTE currently uses StreamIn and
1804 * pasting should always be undoable but streaming isn't always.
1806 * cases to test:
1807 * StreamIn plain text without SFF_SELECTION.
1808 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1809 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1810 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1811 * Feel free to add tests for other text modes or StreamIn things.
1815 HWND hwndRichEdit = new_richedit(NULL);
1816 LRESULT result;
1817 EDITSTREAM es;
1818 char buffer[1024] = {0};
1819 const char randomtext[] = "Some text";
1821 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1823 /* StreamIn, no SFF_SELECTION */
1824 es.dwCookie = nCallbackCount;
1825 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1827 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1828 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1829 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1830 result = strcmp (buffer,"test");
1831 ok (result == 0,
1832 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1834 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1835 ok (result == FALSE,
1836 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1838 /* StreamIn, SFF_SELECTION, but nothing selected */
1839 es.dwCookie = nCallbackCount;
1840 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1841 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1842 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1843 SendMessage(hwndRichEdit, EM_STREAMIN,
1844 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1845 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1846 result = strcmp (buffer,"testSome text");
1847 ok (result == 0,
1848 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1850 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1851 ok (result == TRUE,
1852 "EM_STREAMIN with SFF_SELECTION but no selection set "
1853 "should create an undo\n");
1855 /* StreamIn, SFF_SELECTION, with a selection */
1856 es.dwCookie = nCallbackCount;
1857 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1858 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1859 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1860 SendMessage(hwndRichEdit, EM_STREAMIN,
1861 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1862 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1863 result = strcmp (buffer,"Sometesttext");
1864 ok (result == 0,
1865 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1867 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1868 ok (result == TRUE,
1869 "EM_STREAMIN with SFF_SELECTION and selection set "
1870 "should create an undo\n");
1874 static BOOL is_em_settextex_supported(HWND hwnd)
1876 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
1877 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
1880 static void test_unicode_conversions(void)
1882 static const WCHAR tW[] = {'t',0};
1883 static const WCHAR teW[] = {'t','e',0};
1884 static const WCHAR textW[] = {'t','e','s','t',0};
1885 static const char textA[] = "test";
1886 char bufA[64];
1887 WCHAR bufW[64];
1888 HWND hwnd;
1889 int is_win9x, em_settextex_supported, ret;
1891 is_win9x = GetVersion() & 0x80000000;
1893 #define set_textA(hwnd, wm_set_text, txt) \
1894 do { \
1895 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1896 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1897 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1898 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1899 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1900 } while(0)
1901 #define expect_textA(hwnd, wm_get_text, txt) \
1902 do { \
1903 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1904 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1905 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1906 memset(bufA, 0xAA, sizeof(bufA)); \
1907 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1908 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1909 ret = lstrcmpA(bufA, txt); \
1910 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1911 } while(0)
1913 #define set_textW(hwnd, wm_set_text, txt) \
1914 do { \
1915 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1916 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1917 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1918 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1919 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1920 } while(0)
1921 #define expect_textW(hwnd, wm_get_text, txt) \
1922 do { \
1923 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1924 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1925 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1926 memset(bufW, 0xAA, sizeof(bufW)); \
1927 if (is_win9x) \
1929 assert(wm_get_text == EM_GETTEXTEX); \
1930 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1931 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1933 else \
1935 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1936 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1938 ret = lstrcmpW(bufW, txt); \
1939 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1940 } while(0)
1941 #define expect_empty(hwnd, wm_get_text) \
1942 do { \
1943 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1944 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1945 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1946 memset(bufA, 0xAA, sizeof(bufA)); \
1947 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1948 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1949 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1950 } while(0)
1952 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1953 0, 0, 200, 60, 0, 0, 0, 0);
1954 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1956 ret = IsWindowUnicode(hwnd);
1957 if (is_win9x)
1958 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1959 else
1960 ok(ret, "RichEdit20W should be unicode under NT\n");
1962 /* EM_SETTEXTEX is supported starting from version 3.0 */
1963 em_settextex_supported = is_em_settextex_supported(hwnd);
1964 trace("EM_SETTEXTEX is %ssupported on this platform\n",
1965 em_settextex_supported ? "" : "NOT ");
1967 expect_empty(hwnd, WM_GETTEXT);
1968 expect_empty(hwnd, EM_GETTEXTEX);
1970 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1971 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1972 expect_textA(hwnd, WM_GETTEXT, "t");
1973 expect_textA(hwnd, EM_GETTEXTEX, "t");
1974 expect_textW(hwnd, EM_GETTEXTEX, tW);
1976 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1977 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1978 expect_textA(hwnd, WM_GETTEXT, "te");
1979 expect_textA(hwnd, EM_GETTEXTEX, "te");
1980 expect_textW(hwnd, EM_GETTEXTEX, teW);
1982 set_textA(hwnd, WM_SETTEXT, NULL);
1983 expect_empty(hwnd, WM_GETTEXT);
1984 expect_empty(hwnd, EM_GETTEXTEX);
1986 if (is_win9x)
1987 set_textA(hwnd, WM_SETTEXT, textW);
1988 else
1989 set_textA(hwnd, WM_SETTEXT, textA);
1990 expect_textA(hwnd, WM_GETTEXT, textA);
1991 expect_textA(hwnd, EM_GETTEXTEX, textA);
1992 expect_textW(hwnd, EM_GETTEXTEX, textW);
1994 if (em_settextex_supported)
1996 set_textA(hwnd, EM_SETTEXTEX, textA);
1997 expect_textA(hwnd, WM_GETTEXT, textA);
1998 expect_textA(hwnd, EM_GETTEXTEX, textA);
1999 expect_textW(hwnd, EM_GETTEXTEX, textW);
2002 if (!is_win9x)
2004 set_textW(hwnd, WM_SETTEXT, textW);
2005 expect_textW(hwnd, WM_GETTEXT, textW);
2006 expect_textA(hwnd, WM_GETTEXT, textA);
2007 expect_textW(hwnd, EM_GETTEXTEX, textW);
2008 expect_textA(hwnd, EM_GETTEXTEX, textA);
2010 if (em_settextex_supported)
2012 set_textW(hwnd, EM_SETTEXTEX, textW);
2013 expect_textW(hwnd, WM_GETTEXT, textW);
2014 expect_textA(hwnd, WM_GETTEXT, textA);
2015 expect_textW(hwnd, EM_GETTEXTEX, textW);
2016 expect_textA(hwnd, EM_GETTEXTEX, textA);
2019 DestroyWindow(hwnd);
2021 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
2022 0, 0, 200, 60, 0, 0, 0, 0);
2023 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2025 ret = IsWindowUnicode(hwnd);
2026 ok(!ret, "RichEdit20A should NOT be unicode\n");
2028 set_textA(hwnd, WM_SETTEXT, textA);
2029 expect_textA(hwnd, WM_GETTEXT, textA);
2030 expect_textA(hwnd, EM_GETTEXTEX, textA);
2031 expect_textW(hwnd, EM_GETTEXTEX, textW);
2033 if (em_settextex_supported)
2035 set_textA(hwnd, EM_SETTEXTEX, textA);
2036 expect_textA(hwnd, WM_GETTEXT, textA);
2037 expect_textA(hwnd, EM_GETTEXTEX, textA);
2038 expect_textW(hwnd, EM_GETTEXTEX, textW);
2041 if (!is_win9x)
2043 set_textW(hwnd, WM_SETTEXT, textW);
2044 expect_textW(hwnd, WM_GETTEXT, textW);
2045 expect_textA(hwnd, WM_GETTEXT, textA);
2046 expect_textW(hwnd, EM_GETTEXTEX, textW);
2047 expect_textA(hwnd, EM_GETTEXTEX, textA);
2049 if (em_settextex_supported)
2051 set_textW(hwnd, EM_SETTEXTEX, textW);
2052 expect_textW(hwnd, WM_GETTEXT, textW);
2053 expect_textA(hwnd, WM_GETTEXT, textA);
2054 expect_textW(hwnd, EM_GETTEXTEX, textW);
2055 expect_textA(hwnd, EM_GETTEXTEX, textA);
2058 DestroyWindow(hwnd);
2061 static void test_WM_CHAR(void)
2063 HWND hwnd;
2064 int ret;
2065 const char * char_list = "abc\rabc\r";
2066 const char * expected_content_single = "abcabc";
2067 const char * expected_content_multi = "abc\r\nabc\r\n";
2068 char buffer[64] = {0};
2069 const char * p;
2071 /* single-line control must IGNORE carriage returns */
2072 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2073 0, 0, 200, 60, 0, 0, 0, 0);
2074 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2076 p = char_list;
2077 while (*p != '\0') {
2078 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2079 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2080 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2081 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2082 p++;
2085 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2086 ret = strcmp(buffer, expected_content_single);
2087 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2089 DestroyWindow(hwnd);
2091 /* multi-line control inserts CR normally */
2092 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
2093 0, 0, 200, 60, 0, 0, 0, 0);
2094 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2096 p = char_list;
2097 while (*p != '\0') {
2098 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2099 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2100 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2101 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2102 p++;
2105 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2106 ret = strcmp(buffer, expected_content_multi);
2107 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2109 DestroyWindow(hwnd);
2112 static void test_EM_GETTEXTLENGTHEX(void)
2114 HWND hwnd;
2115 GETTEXTLENGTHEX gtl;
2116 int ret;
2117 const char * test_string = "a\nb\n\n\r\n";
2118 const char * test_string_after = "a";
2119 char buffer[64] = {0};
2121 /* single line */
2122 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2123 0, 0, 200, 60, 0, 0, 0, 0);
2124 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2126 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2127 gtl.codepage = CP_ACP;
2128 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2129 ok(ret == 0, "ret %d\n",ret);
2131 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2132 gtl.codepage = CP_ACP;
2133 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2134 ok(ret == 0, "ret %d\n",ret);
2136 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
2138 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2139 gtl.codepage = CP_ACP;
2140 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2141 ok(ret == 1, "ret %d\n",ret);
2143 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2144 gtl.codepage = CP_ACP;
2145 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2146 ok(ret == 1, "ret %d\n",ret);
2148 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2149 ret = strcmp(buffer, test_string_after);
2150 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2152 DestroyWindow(hwnd);
2154 /* multi line */
2155 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
2156 0, 0, 200, 60, 0, 0, 0, 0);
2157 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2159 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2160 gtl.codepage = CP_ACP;
2161 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2162 todo_wine ok(ret == 0, "ret %d\n",ret);
2164 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2165 gtl.codepage = CP_ACP;
2166 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2167 ok(ret == 0, "ret %d\n",ret);
2169 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
2171 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2172 gtl.codepage = CP_ACP;
2173 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2174 todo_wine ok(ret == 10, "ret %d\n",ret);
2176 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2177 gtl.codepage = CP_ACP;
2178 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2179 ok(ret == 6, "ret %d\n",ret);
2181 DestroyWindow(hwnd);
2185 /* globals that parent and child access when checking event masks & notifications */
2186 static HWND eventMaskEditHwnd = 0;
2187 static int queriedEventMask;
2188 static int watchForEventMask = 0;
2190 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
2191 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2193 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
2195 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2197 return DefWindowProcA(hwnd, message, wParam, lParam);
2200 /* test event masks in combination with WM_COMMAND */
2201 static void test_eventMask(void)
2203 HWND parent;
2204 int ret;
2205 WNDCLASSA cls;
2206 const char text[] = "foo bar\n";
2207 int eventMask;
2209 /* register class to capture WM_COMMAND */
2210 cls.style = 0;
2211 cls.lpfnWndProc = ParentMsgCheckProcA;
2212 cls.cbClsExtra = 0;
2213 cls.cbWndExtra = 0;
2214 cls.hInstance = GetModuleHandleA(0);
2215 cls.hIcon = 0;
2216 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
2217 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
2218 cls.lpszMenuName = NULL;
2219 cls.lpszClassName = "EventMaskParentClass";
2220 if(!RegisterClassA(&cls)) assert(0);
2222 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
2223 0, 0, 200, 60, NULL, NULL, NULL, NULL);
2224 ok (parent != 0, "Failed to create parent window\n");
2226 eventMaskEditHwnd = new_richedit(parent);
2227 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
2229 eventMask = ENM_CHANGE | ENM_UPDATE;
2230 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
2231 ok(ret == ENM_NONE, "wrong event mask\n");
2232 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2233 ok(ret == eventMask, "failed to set event mask\n");
2235 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
2236 queriedEventMask = 0; /* initialize to something other than we expect */
2237 watchForEventMask = EN_CHANGE;
2238 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
2239 ok(ret == TRUE, "failed to set text\n");
2240 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
2241 notification in response to WM_SETTEXT */
2242 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
2243 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
2248 START_TEST( editor )
2250 MSG msg;
2251 time_t end;
2253 /* Must explicitly LoadLibrary(). The test has no references to functions in
2254 * RICHED20.DLL, so the linker doesn't actually link to it. */
2255 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
2256 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
2258 test_WM_CHAR();
2259 test_EM_FINDTEXT();
2260 test_EM_GETLINE();
2261 test_EM_SCROLLCARET();
2262 test_EM_SCROLL();
2263 test_WM_SETTEXT();
2264 test_EM_SETCHARFORMAT();
2265 test_EM_SETTEXTMODE();
2266 test_TM_PLAINTEXT();
2267 test_EM_SETOPTIONS();
2268 test_WM_GETTEXT();
2269 test_EM_AUTOURLDETECT();
2270 test_EM_SETUNDOLIMIT();
2271 test_ES_PASSWORD();
2272 test_EM_SETTEXTEX();
2273 test_EM_LIMITTEXT();
2274 test_EM_EXLIMITTEXT();
2275 test_EM_GETLIMITTEXT();
2276 test_WM_SETFONT();
2277 test_EM_GETMODIFY();
2278 test_EM_EXSETSEL();
2279 test_WM_PASTE();
2280 test_EM_StreamIn_Undo();
2281 test_EM_FORMATRANGE();
2282 test_unicode_conversions();
2283 test_EM_GETTEXTLENGTHEX();
2284 test_EM_REPLACESEL();
2285 test_eventMask();
2287 /* Set the environment variable WINETEST_RICHED20 to keep windows
2288 * responsive and open for 30 seconds. This is useful for debugging.
2290 * The message pump uses PeekMessage() to empty the queue and then sleeps for
2291 * 50ms before retrying the queue. */
2292 end = time(NULL) + 30;
2293 if (getenv( "WINETEST_RICHED20" )) {
2294 while (time(NULL) < end) {
2295 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
2296 TranslateMessage(&msg);
2297 DispatchMessage(&msg);
2298 } else {
2299 Sleep(50);
2304 OleFlushClipboard();
2305 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());