riched20: Allow for Win98 behavior for return value of EM_REPLACESEL.
[wine/multimedia.git] / dlls / riched20 / tests / editor.c
blob670096e018534895e7998e3500c5549bbd6ec712
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 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
1007 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
1008 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
1009 const char * TestItem5_after = "TestSomeText TestSomeText";
1010 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
1011 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
1012 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
1013 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
1014 char buf[1024] = {0};
1015 LRESULT result;
1017 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
1018 any solitary \r to be converted to \r\n on return. Properly paired
1019 \r\n are not affected. It also shows that the special sequence \r\r\n
1020 gets converted to a single space.
1023 #define TEST_SETTEXT(a, b) \
1024 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
1025 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
1026 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
1027 ok (result == strlen(buf), \
1028 "WM_GETTEXT returned %ld instead of expected %u\n", \
1029 result, strlen(buf)); \
1030 result = strcmp(b, buf); \
1031 ok(result == 0, \
1032 "WM_SETTEXT round trip: strcmp = %ld\n", result);
1034 TEST_SETTEXT(TestItem1, TestItem1)
1035 TEST_SETTEXT(TestItem2, TestItem2_after)
1036 TEST_SETTEXT(TestItem3, TestItem3_after)
1037 TEST_SETTEXT(TestItem3_after, TestItem3_after)
1038 TEST_SETTEXT(TestItem4, TestItem4_after)
1039 TEST_SETTEXT(TestItem5, TestItem5_after)
1040 TEST_SETTEXT(TestItem6, TestItem6_after)
1041 TEST_SETTEXT(TestItem7, TestItem7_after)
1043 #undef TEST_SETTEXT
1044 DestroyWindow(hwndRichEdit);
1047 static void test_EM_SETTEXTEX(void)
1049 HWND hwndRichEdit = new_richedit(NULL);
1050 SETTEXTEX setText;
1051 GETTEXTEX getText;
1052 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1053 'S', 'o', 'm', 'e',
1054 'T', 'e', 'x', 't', 0};
1055 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1056 'S', 'o', 'm', 'e',
1057 'T', 'e', 'x', 't',
1058 '\r', 0};
1059 const char * TestItem2_after = "TestSomeText\r\n";
1060 WCHAR TestItem3[] = {'T', 'e', 's', 't',
1061 'S', 'o', 'm', 'e',
1062 'T', 'e', 'x', 't',
1063 '\r','\n','\r','\n', 0};
1064 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
1065 'S', 'o', 'm', 'e',
1066 'T', 'e', 'x', 't',
1067 '\n','\n', 0};
1068 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
1069 'S', 'o', 'm', 'e',
1070 'T', 'e', 'x', 't',
1071 '\r','\r', 0};
1072 WCHAR TestItem4[] = {'T', 'e', 's', 't',
1073 'S', 'o', 'm', 'e',
1074 'T', 'e', 'x', 't',
1075 '\r','\r','\n','\r',
1076 '\n', 0};
1077 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
1078 'S', 'o', 'm', 'e',
1079 'T', 'e', 'x', 't',
1080 ' ','\r', 0};
1081 #define MAX_BUF_LEN 1024
1082 WCHAR buf[MAX_BUF_LEN];
1083 int result;
1084 CHARRANGE cr;
1086 setText.codepage = 1200; /* no constant for unicode */
1087 getText.codepage = 1200; /* no constant for unicode */
1088 getText.cb = MAX_BUF_LEN;
1089 getText.flags = GT_DEFAULT;
1090 getText.lpDefaultChar = NULL;
1091 getText.lpUsedDefaultChar = NULL;
1093 setText.flags = 0;
1094 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1095 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1096 ok(lstrcmpW(buf, TestItem1) == 0,
1097 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1099 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
1100 convert \r to \r\n on return
1102 setText.codepage = 1200; /* no constant for unicode */
1103 getText.codepage = 1200; /* no constant for unicode */
1104 getText.cb = MAX_BUF_LEN;
1105 getText.flags = GT_DEFAULT;
1106 getText.lpDefaultChar = NULL;
1107 getText.lpUsedDefaultChar = NULL;
1108 setText.flags = 0;
1109 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
1110 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1111 ok(lstrcmpW(buf, TestItem2) == 0,
1112 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
1114 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
1115 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
1116 ok(strcmp((const char *)buf, TestItem2_after) == 0,
1117 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
1119 /* \r\n pairs get changed into \r */
1120 setText.codepage = 1200; /* no constant for unicode */
1121 getText.codepage = 1200; /* no constant for unicode */
1122 getText.cb = MAX_BUF_LEN;
1123 getText.flags = GT_DEFAULT;
1124 getText.lpDefaultChar = NULL;
1125 getText.lpUsedDefaultChar = NULL;
1126 setText.flags = 0;
1127 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
1128 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1129 ok(lstrcmpW(buf, TestItem3_after) == 0,
1130 "EM_SETTEXTEX did not convert properly\n");
1132 /* \n also gets changed to \r */
1133 setText.codepage = 1200; /* no constant for unicode */
1134 getText.codepage = 1200; /* no constant for unicode */
1135 getText.cb = MAX_BUF_LEN;
1136 getText.flags = GT_DEFAULT;
1137 getText.lpDefaultChar = NULL;
1138 getText.lpUsedDefaultChar = NULL;
1139 setText.flags = 0;
1140 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
1141 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1142 ok(lstrcmpW(buf, TestItem3_after) == 0,
1143 "EM_SETTEXTEX did not convert properly\n");
1145 /* \r\r\n gets changed into single space */
1146 setText.codepage = 1200; /* no constant for unicode */
1147 getText.codepage = 1200; /* no constant for unicode */
1148 getText.cb = MAX_BUF_LEN;
1149 getText.flags = GT_DEFAULT;
1150 getText.lpDefaultChar = NULL;
1151 getText.lpUsedDefaultChar = NULL;
1152 setText.flags = 0;
1153 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
1154 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1155 ok(lstrcmpW(buf, TestItem4_after) == 0,
1156 "EM_SETTEXTEX did not convert properly\n");
1158 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1159 (WPARAM)&setText, (LPARAM) NULL);
1160 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1162 ok (result == 1,
1163 "EM_SETTEXTEX returned %d, instead of 1\n",result);
1164 ok(lstrlenW(buf) == 0,
1165 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
1167 /* put some text back */
1168 setText.flags = 0;
1169 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1170 /* select some text */
1171 cr.cpMax = 1;
1172 cr.cpMin = 3;
1173 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1174 /* replace current selection */
1175 setText.flags = ST_SELECTION;
1176 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1177 (WPARAM)&setText, (LPARAM) NULL);
1178 ok(result == 0,
1179 "EM_SETTEXTEX with NULL lParam to replace selection"
1180 " with no text should return 0. Got %i\n",
1181 result);
1183 /* put some text back */
1184 setText.flags = 0;
1185 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
1186 /* select some text */
1187 cr.cpMax = 1;
1188 cr.cpMin = 3;
1189 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1190 /* replace current selection */
1191 setText.flags = ST_SELECTION;
1192 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
1193 (WPARAM)&setText, (LPARAM) TestItem1);
1194 /* get text */
1195 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1196 ok(result == lstrlenW(TestItem1),
1197 "EM_SETTEXTEX with NULL lParam to replace selection"
1198 " with no text should return 0. Got %i\n",
1199 result);
1200 ok(lstrlenW(buf) == 22,
1201 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
1202 lstrlenW(buf) );
1204 DestroyWindow(hwndRichEdit);
1207 static void test_EM_LIMITTEXT(void)
1209 int ret;
1211 HWND hwndRichEdit = new_richedit(NULL);
1213 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
1214 * about setting the length to -1 for multiline edit controls doesn't happen.
1217 /* Don't check default gettextlimit case. That's done in other tests */
1219 /* Set textlimit to 100 */
1220 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1221 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1222 ok (ret == 100,
1223 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1225 /* Set textlimit to 0 */
1226 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1227 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1228 ok (ret == 65536,
1229 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1231 /* Set textlimit to -1 */
1232 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1233 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1234 ok (ret == -1,
1235 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1237 /* Set textlimit to -2 */
1238 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1239 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1240 ok (ret == -2,
1241 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1243 DestroyWindow (hwndRichEdit);
1247 static void test_EM_EXLIMITTEXT(void)
1249 int i, selBegin, selEnd, len1, len2;
1250 int result;
1251 char text[1024 + 1];
1252 char buffer[1024 + 1];
1253 int textlimit = 0; /* multiple of 100 */
1254 HWND hwndRichEdit = new_richedit(NULL);
1256 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1257 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1259 textlimit = 256000;
1260 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1261 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1262 /* set higher */
1263 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1265 textlimit = 1000;
1266 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1267 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1268 /* set lower */
1269 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1271 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1272 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1273 /* default for WParam = 0 */
1274 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1276 textlimit = sizeof(text)-1;
1277 memset(text, 'W', textlimit);
1278 text[sizeof(text)-1] = 0;
1279 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1280 /* maxed out text */
1281 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1283 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1284 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1285 len1 = selEnd - selBegin;
1287 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1288 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1289 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1290 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1291 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1292 len2 = selEnd - selBegin;
1294 ok(len1 != len2,
1295 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1296 len1,len2,i);
1298 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1299 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1300 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1301 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1302 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1303 len1 = selEnd - selBegin;
1305 ok(len1 != len2,
1306 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1307 len1,len2,i);
1309 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1310 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1311 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1312 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1313 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1314 len2 = selEnd - selBegin;
1316 ok(len1 == len2,
1317 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1318 len1,len2,i);
1320 /* set text up to the limit, select all the text, then add a char */
1321 textlimit = 5;
1322 memset(text, 'W', textlimit);
1323 text[textlimit] = 0;
1324 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1325 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1326 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1327 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1328 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1329 result = strcmp(buffer, "A");
1330 ok(0 == result, "got string = \"%s\"\n", buffer);
1332 /* WM_SETTEXT not limited */
1333 textlimit = 10;
1334 memset(text, 'W', textlimit);
1335 text[textlimit] = 0;
1336 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
1337 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1338 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1339 i = strlen(buffer);
1340 ok(10 == i, "expected 10 chars\n");
1341 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1342 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1344 /* try inserting more text at end */
1345 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1346 ok(0 == i, "WM_CHAR wasn't processed\n");
1347 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1348 i = strlen(buffer);
1349 ok(10 == i, "expected 10 chars, got %i\n", i);
1350 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1351 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1353 /* try inserting text at beginning */
1354 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
1355 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1356 ok(0 == i, "WM_CHAR wasn't processed\n");
1357 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1358 i = strlen(buffer);
1359 ok(10 == i, "expected 10 chars, got %i\n", i);
1360 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1361 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
1363 /* WM_CHAR is limited */
1364 textlimit = 1;
1365 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1366 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1367 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1368 ok(0 == i, "WM_CHAR wasn't processed\n");
1369 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1370 ok(0 == i, "WM_CHAR wasn't processed\n");
1371 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1372 i = strlen(buffer);
1373 ok(1 == i, "expected 1 chars, got %i instead\n", i);
1375 DestroyWindow(hwndRichEdit);
1378 static void test_EM_GETLIMITTEXT(void)
1380 int i;
1381 HWND hwndRichEdit = new_richedit(NULL);
1383 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1384 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1386 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1387 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1388 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1390 DestroyWindow(hwndRichEdit);
1393 static void test_WM_SETFONT(void)
1395 /* There is no invalid input or error conditions for this function.
1396 * NULL wParam and lParam just fall back to their default values
1397 * It should be noted that even if you use a gibberish name for your fonts
1398 * here, it will still work because the name is stored. They will display as
1399 * System, but will report their name to be whatever they were created as */
1401 HWND hwndRichEdit = new_richedit(NULL);
1402 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1403 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1404 FF_DONTCARE, "Marlett");
1405 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1406 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1407 FF_DONTCARE, "MS Sans Serif");
1408 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1409 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1410 FF_DONTCARE, "Courier");
1411 LOGFONTA sentLogFont;
1412 CHARFORMAT2A returnedCF2A;
1414 returnedCF2A.cbSize = sizeof(returnedCF2A);
1416 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1417 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1418 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1420 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1421 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1422 "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1423 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1425 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1426 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1427 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1428 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1429 "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1430 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1432 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1433 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1434 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1435 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1436 "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1437 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1439 /* This last test is special since we send in NULL. We clear the variables
1440 * and just compare to "System" instead of the sent in font name. */
1441 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1442 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1443 returnedCF2A.cbSize = sizeof(returnedCF2A);
1445 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1446 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1447 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1448 ok (!strcmp("System",returnedCF2A.szFaceName),
1449 "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1451 DestroyWindow(hwndRichEdit);
1455 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1456 LPBYTE pbBuff,
1457 LONG cb,
1458 LONG *pcb)
1460 const char** str = (const char**)dwCookie;
1461 int size = strlen(*str);
1462 if(size > 3) /* let's make it peice-meal for fun */
1463 size = 3;
1464 *pcb = cb;
1465 if (*pcb > size) {
1466 *pcb = size;
1468 if (*pcb > 0) {
1469 memcpy(pbBuff, *str, *pcb);
1470 *str += *pcb;
1472 return 0;
1475 static void test_EM_GETMODIFY(void)
1477 HWND hwndRichEdit = new_richedit(NULL);
1478 LRESULT result;
1479 SETTEXTEX setText;
1480 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1481 'S', 'o', 'm', 'e',
1482 'T', 'e', 'x', 't', 0};
1483 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1484 'S', 'o', 'm', 'e',
1485 'O', 't', 'h', 'e', 'r',
1486 'T', 'e', 'x', 't', 0};
1487 const char* streamText = "hello world";
1488 CHARFORMAT2 cf2;
1489 PARAFORMAT2 pf2;
1490 EDITSTREAM es;
1492 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1493 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1494 FF_DONTCARE, "Courier");
1496 setText.codepage = 1200; /* no constant for unicode */
1497 setText.flags = ST_KEEPUNDO;
1500 /* modify flag shouldn't be set when richedit is first created */
1501 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1502 ok (result == 0,
1503 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1505 /* setting modify flag should actually set it */
1506 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1507 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1508 ok (result != 0,
1509 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1511 /* clearing modify flag should actually clear it */
1512 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1513 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1514 ok (result == 0,
1515 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1517 /* setting font doesn't change modify flag */
1518 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1519 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1520 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1521 ok (result == 0,
1522 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1524 /* setting text should set modify flag */
1525 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1526 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1527 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1528 ok (result != 0,
1529 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1531 /* undo previous text doesn't reset modify flag */
1532 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1533 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1534 ok (result != 0,
1535 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1537 /* set text with no flag to keep undo stack should not set modify flag */
1538 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1539 setText.flags = 0;
1540 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1541 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1542 ok (result == 0,
1543 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1545 /* WM_SETTEXT doesn't modify */
1546 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1547 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1548 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1549 todo_wine {
1550 ok (result == 0,
1551 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1554 /* clear the text */
1555 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1556 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1557 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1558 ok (result == 0,
1559 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1561 /* replace text */
1562 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1563 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1564 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1565 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1566 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1567 ok (result != 0,
1568 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1570 /* copy/paste text 1 */
1571 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1572 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1573 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1574 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1575 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1576 ok (result != 0,
1577 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1579 /* copy/paste text 2 */
1580 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1581 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1582 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1583 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1584 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1585 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1586 ok (result != 0,
1587 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1589 /* press char */
1590 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1591 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1592 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1593 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1594 ok (result != 0,
1595 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1597 /* press del */
1598 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1599 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1600 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1601 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1602 ok (result != 0,
1603 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1605 /* set char format */
1606 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1607 cf2.cbSize = sizeof(CHARFORMAT2);
1608 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1609 (LPARAM) &cf2);
1610 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1611 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1612 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1613 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1614 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
1615 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1616 ok (result != 0,
1617 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1619 /* set para format */
1620 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1621 pf2.cbSize = sizeof(PARAFORMAT2);
1622 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1623 (LPARAM) &pf2);
1624 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1625 pf2.wAlignment = PFA_RIGHT;
1626 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1627 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1628 ok (result == 0,
1629 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1631 /* EM_STREAM */
1632 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1633 es.dwCookie = (DWORD_PTR)&streamText;
1634 es.dwError = 0;
1635 es.pfnCallback = test_EM_GETMODIFY_esCallback;
1636 SendMessage(hwndRichEdit, EM_STREAMIN,
1637 (WPARAM)(SF_TEXT), (LPARAM)&es);
1638 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1639 ok (result != 0,
1640 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1642 DestroyWindow(hwndRichEdit);
1645 struct exsetsel_s {
1646 long min;
1647 long max;
1648 long expected_retval;
1649 int expected_getsel_start;
1650 int expected_getsel_end;
1651 int _exsetsel_todo_wine;
1652 int _getsel_todo_wine;
1655 const struct exsetsel_s exsetsel_tests[] = {
1656 /* sanity tests */
1657 {5, 10, 10, 5, 10, 0, 0},
1658 {15, 17, 17, 15, 17, 0, 0},
1659 /* test cpMax > strlen() */
1660 {0, 100, 18, 0, 18, 0, 1},
1661 /* test cpMin == cpMax */
1662 {5, 5, 5, 5, 5, 0, 0},
1663 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1664 {-1, 0, 5, 5, 5, 0, 0},
1665 {-1, 17, 5, 5, 5, 0, 0},
1666 {-1, 18, 5, 5, 5, 0, 0},
1667 /* test cpMin < 0 && cpMax < 0 */
1668 {-1, -1, 17, 17, 17, 0, 0},
1669 {-4, -5, 17, 17, 17, 0, 0},
1670 /* test cMin >=0 && cpMax < 0 (bug 6814) */
1671 {0, -1, 18, 0, 18, 0, 1},
1672 {17, -5, 18, 17, 18, 0, 1},
1673 {18, -3, 17, 17, 17, 0, 0},
1674 /* test if cpMin > cpMax */
1675 {15, 19, 18, 15, 18, 0, 1},
1676 {19, 15, 18, 15, 18, 0, 1}
1679 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1680 CHARRANGE cr;
1681 long result;
1682 int start, end;
1684 cr.cpMin = setsel->min;
1685 cr.cpMax = setsel->max;
1686 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1688 if (setsel->_exsetsel_todo_wine) {
1689 todo_wine {
1690 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1692 } else {
1693 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1696 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1698 if (setsel->_getsel_todo_wine) {
1699 todo_wine {
1700 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);
1702 } else {
1703 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);
1707 static void test_EM_EXSETSEL(void)
1709 HWND hwndRichEdit = new_richedit(NULL);
1710 int i;
1711 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1713 /* sending some text to the window */
1714 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1715 /* 01234567890123456*/
1716 /* 10 */
1718 for (i = 0; i < num_tests; i++) {
1719 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1722 DestroyWindow(hwndRichEdit);
1725 static void test_EM_REPLACESEL(void)
1727 HWND hwndRichEdit = new_richedit(NULL);
1728 char buffer[1024] = {0};
1729 int r;
1730 GETTEXTEX getText;
1731 CHARRANGE cr;
1733 /* sending some text to the window */
1734 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1735 /* 01234567890123456*/
1736 /* 10 */
1738 /* FIXME add more tests */
1739 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
1740 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
1741 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
1742 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1743 r = strcmp(buffer, "testing");
1744 ok(0 == r, "expected %d, got %d\n", 0, r);
1746 DestroyWindow(hwndRichEdit);
1748 hwndRichEdit = new_richedit(NULL);
1750 /* Test behavior with carriage returns and newlines */
1751 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1752 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
1753 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
1754 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1755 r = strcmp(buffer, "RichEdit1");
1756 ok(0 == r, "expected %d, got %d\n", 0, r);
1757 getText.cb = 1024;
1758 getText.codepage = CP_ACP;
1759 getText.flags = GT_DEFAULT;
1760 getText.lpDefaultChar = NULL;
1761 getText.lpUsedDefaultChar = NULL;
1762 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1763 ok(strcmp(buffer, "RichEdit1") == 0,
1764 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
1766 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1767 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
1768 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
1769 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1770 r = strcmp(buffer, "RichEdit1\r\n");
1771 ok(0 == r, "expected %d, got %d\n", 0, r);
1772 getText.cb = 1024;
1773 getText.codepage = CP_ACP;
1774 getText.flags = GT_DEFAULT;
1775 getText.lpDefaultChar = NULL;
1776 getText.lpUsedDefaultChar = NULL;
1777 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1778 ok(strcmp(buffer, "RichEdit1\r") == 0,
1779 "EM_GETTEXTEX returned incorrect string\n");
1781 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
1782 EM_REPLACESEL. The general rule seems to be that Win98's riched20
1783 returns the number of characters *inserted* into the control (after
1784 required conversions), but WinXP's riched20 returns the number of
1785 characters interpreted from the original lParam. Wine's builtin riched20
1786 implements the WinXP behavior.
1788 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1789 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
1790 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
1791 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
1793 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1794 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1795 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
1796 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
1798 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1799 r = strcmp(buffer, "RichEdit1\r\n");
1800 ok(0 == r, "expected %d, got %d\n", 0, r);
1801 getText.cb = 1024;
1802 getText.codepage = CP_ACP;
1803 getText.flags = GT_DEFAULT;
1804 getText.lpDefaultChar = NULL;
1805 getText.lpUsedDefaultChar = NULL;
1806 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1807 ok(strcmp(buffer, "RichEdit1\r") == 0,
1808 "EM_GETTEXTEX returned incorrect string\n");
1810 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1811 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1812 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
1813 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
1815 /* The following tests show that richedit should handle the special \r\r\n
1816 sequence by turning it into a single space on insertion. However,
1817 EM_REPLACESEL on WinXP returns the number of characters in the original
1818 string.
1821 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1822 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
1823 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
1824 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1825 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1826 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
1827 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
1829 /* Test the actual string */
1830 getText.cb = 1024;
1831 getText.codepage = CP_ACP;
1832 getText.flags = GT_DEFAULT;
1833 getText.lpDefaultChar = NULL;
1834 getText.lpUsedDefaultChar = NULL;
1835 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1836 ok(strcmp(buffer, "\r\r") == 0,
1837 "EM_GETTEXTEX returned incorrect string\n");
1839 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1840 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
1841 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
1842 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
1843 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1844 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1845 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
1846 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
1848 /* Test the actual string */
1849 getText.cb = 1024;
1850 getText.codepage = CP_ACP;
1851 getText.flags = GT_DEFAULT;
1852 getText.lpDefaultChar = NULL;
1853 getText.lpUsedDefaultChar = NULL;
1854 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1855 ok(strcmp(buffer, " ") == 0,
1856 "EM_GETTEXTEX returned incorrect string\n");
1858 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1859 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
1860 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
1861 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
1862 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1863 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1864 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
1865 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
1867 /* Test the actual string */
1868 getText.cb = 1024;
1869 getText.codepage = CP_ACP;
1870 getText.flags = GT_DEFAULT;
1871 getText.lpDefaultChar = NULL;
1872 getText.lpUsedDefaultChar = NULL;
1873 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1874 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
1875 "EM_GETTEXTEX returned incorrect string\n");
1877 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1878 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
1879 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
1880 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
1881 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1882 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1883 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
1884 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
1886 /* Test the actual string */
1887 getText.cb = 1024;
1888 getText.codepage = CP_ACP;
1889 getText.flags = GT_DEFAULT;
1890 getText.lpDefaultChar = NULL;
1891 getText.lpUsedDefaultChar = NULL;
1892 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1893 ok(strcmp(buffer, " \r") == 0,
1894 "EM_GETTEXTEX returned incorrect string\n");
1896 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1897 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
1898 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
1899 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
1900 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1901 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1902 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
1903 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
1905 /* Test the actual string */
1906 getText.cb = 1024;
1907 getText.codepage = CP_ACP;
1908 getText.flags = GT_DEFAULT;
1909 getText.lpDefaultChar = NULL;
1910 getText.lpUsedDefaultChar = NULL;
1911 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1912 ok(strcmp(buffer, " \r\r") == 0,
1913 "EM_GETTEXTEX returned incorrect string\n");
1915 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1916 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
1917 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
1918 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
1919 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1920 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1921 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
1922 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
1924 /* Test the actual string */
1925 getText.cb = 1024;
1926 getText.codepage = CP_ACP;
1927 getText.flags = GT_DEFAULT;
1928 getText.lpDefaultChar = NULL;
1929 getText.lpUsedDefaultChar = NULL;
1930 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1931 ok(strcmp(buffer, "\rX\r\r\r") == 0,
1932 "EM_GETTEXTEX returned incorrect string\n");
1934 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1935 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
1936 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
1937 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1938 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1939 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
1940 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
1942 /* Test the actual string */
1943 getText.cb = 1024;
1944 getText.codepage = CP_ACP;
1945 getText.flags = GT_DEFAULT;
1946 getText.lpDefaultChar = NULL;
1947 getText.lpUsedDefaultChar = NULL;
1948 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1949 ok(strcmp(buffer, "\r\r") == 0,
1950 "EM_GETTEXTEX returned incorrect string\n");
1952 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
1953 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
1954 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
1955 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
1956 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1957 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
1958 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
1959 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
1961 /* Test the actual string */
1962 getText.cb = 1024;
1963 getText.codepage = CP_ACP;
1964 getText.flags = GT_DEFAULT;
1965 getText.lpDefaultChar = NULL;
1966 getText.lpUsedDefaultChar = NULL;
1967 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
1968 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
1969 "EM_GETTEXTEX returned incorrect string\n");
1971 DestroyWindow(hwndRichEdit);
1974 static void test_WM_PASTE(void)
1976 int result;
1977 char buffer[1024] = {0};
1978 const char* text1 = "testing paste\r";
1979 const char* text1_after = "testing paste\r\n";
1980 const char* text2 = "testing paste\r\rtesting paste";
1981 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
1982 HWND hwndRichEdit = new_richedit(NULL);
1984 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1985 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1986 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1987 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1988 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1989 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1990 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1991 result = strcmp(text1_after, buffer);
1992 ok(result == 0,
1993 "test paste: strcmp = %i\n", result);
1995 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1996 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1997 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1998 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1999 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
2000 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
2001 SendMessage(hwndRichEdit, WM_CHAR, 25, 0); /* ctrl-y */
2002 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2003 result = strcmp(buffer,text3);
2004 ok(result == 0,
2005 "test paste: strcmp = %i\n", result);
2007 DestroyWindow(hwndRichEdit);
2010 static void test_EM_FORMATRANGE(void)
2012 int r;
2013 FORMATRANGE fr;
2014 HDC hdc;
2015 HWND hwndRichEdit = new_richedit(NULL);
2017 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
2019 hdc = GetDC(hwndRichEdit);
2020 ok(hdc != NULL, "Could not get HDC\n");
2022 fr.hdc = fr.hdcTarget = hdc;
2023 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
2024 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
2025 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
2026 fr.chrg.cpMin = 0;
2027 fr.chrg.cpMax = 20;
2029 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2030 todo_wine {
2031 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2034 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2035 todo_wine {
2036 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
2039 fr.chrg.cpMin = 0;
2040 fr.chrg.cpMax = 10;
2042 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
2043 todo_wine {
2044 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
2047 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
2048 todo_wine {
2049 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
2052 DestroyWindow(hwndRichEdit);
2055 static int nCallbackCount = 0;
2057 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
2058 LONG cb, LONG* pcb)
2060 const char text[] = {'t','e','s','t'};
2062 if (sizeof(text) <= cb)
2064 if ((int)dwCookie != nCallbackCount)
2066 *pcb = 0;
2067 return 0;
2070 memcpy (pbBuff, text, sizeof(text));
2071 *pcb = sizeof(text);
2073 nCallbackCount++;
2075 return 0;
2077 else
2078 return 1; /* indicates callback failed */
2081 static void test_EM_StreamIn_Undo(void)
2083 /* The purpose of this test is to determine when a EM_StreamIn should be
2084 * undoable. This is important because WM_PASTE currently uses StreamIn and
2085 * pasting should always be undoable but streaming isn't always.
2087 * cases to test:
2088 * StreamIn plain text without SFF_SELECTION.
2089 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
2090 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
2091 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
2092 * Feel free to add tests for other text modes or StreamIn things.
2096 HWND hwndRichEdit = new_richedit(NULL);
2097 LRESULT result;
2098 EDITSTREAM es;
2099 char buffer[1024] = {0};
2100 const char randomtext[] = "Some text";
2102 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
2104 /* StreamIn, no SFF_SELECTION */
2105 es.dwCookie = nCallbackCount;
2106 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2107 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2108 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2109 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
2110 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2111 result = strcmp (buffer,"test");
2112 ok (result == 0,
2113 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
2115 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2116 ok (result == FALSE,
2117 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
2119 /* StreamIn, SFF_SELECTION, but nothing selected */
2120 es.dwCookie = nCallbackCount;
2121 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2122 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2123 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
2124 SendMessage(hwndRichEdit, EM_STREAMIN,
2125 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2126 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2127 result = strcmp (buffer,"testSome text");
2128 ok (result == 0,
2129 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2131 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2132 ok (result == TRUE,
2133 "EM_STREAMIN with SFF_SELECTION but no selection set "
2134 "should create an undo\n");
2136 /* StreamIn, SFF_SELECTION, with a selection */
2137 es.dwCookie = nCallbackCount;
2138 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2139 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
2140 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
2141 SendMessage(hwndRichEdit, EM_STREAMIN,
2142 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
2143 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2144 result = strcmp (buffer,"Sometesttext");
2145 ok (result == 0,
2146 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
2148 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
2149 ok (result == TRUE,
2150 "EM_STREAMIN with SFF_SELECTION and selection set "
2151 "should create an undo\n");
2155 static BOOL is_em_settextex_supported(HWND hwnd)
2157 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
2158 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
2161 static void test_unicode_conversions(void)
2163 static const WCHAR tW[] = {'t',0};
2164 static const WCHAR teW[] = {'t','e',0};
2165 static const WCHAR textW[] = {'t','e','s','t',0};
2166 static const char textA[] = "test";
2167 char bufA[64];
2168 WCHAR bufW[64];
2169 HWND hwnd;
2170 int is_win9x, em_settextex_supported, ret;
2172 is_win9x = GetVersion() & 0x80000000;
2174 #define set_textA(hwnd, wm_set_text, txt) \
2175 do { \
2176 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
2177 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2178 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2179 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2180 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
2181 } while(0)
2182 #define expect_textA(hwnd, wm_get_text, txt) \
2183 do { \
2184 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2185 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
2186 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2187 memset(bufA, 0xAA, sizeof(bufA)); \
2188 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2189 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2190 ret = lstrcmpA(bufA, txt); \
2191 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
2192 } while(0)
2194 #define set_textW(hwnd, wm_set_text, txt) \
2195 do { \
2196 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
2197 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
2198 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
2199 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
2200 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
2201 } while(0)
2202 #define expect_textW(hwnd, wm_get_text, txt) \
2203 do { \
2204 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
2205 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
2206 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2207 memset(bufW, 0xAA, sizeof(bufW)); \
2208 if (is_win9x) \
2210 assert(wm_get_text == EM_GETTEXTEX); \
2211 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2212 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
2214 else \
2216 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
2217 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
2219 ret = lstrcmpW(bufW, txt); \
2220 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
2221 } while(0)
2222 #define expect_empty(hwnd, wm_get_text) \
2223 do { \
2224 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
2225 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
2226 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
2227 memset(bufA, 0xAA, sizeof(bufA)); \
2228 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
2229 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
2230 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
2231 } while(0)
2233 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2234 0, 0, 200, 60, 0, 0, 0, 0);
2235 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2237 ret = IsWindowUnicode(hwnd);
2238 if (is_win9x)
2239 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
2240 else
2241 ok(ret, "RichEdit20W should be unicode under NT\n");
2243 /* EM_SETTEXTEX is supported starting from version 3.0 */
2244 em_settextex_supported = is_em_settextex_supported(hwnd);
2245 trace("EM_SETTEXTEX is %ssupported on this platform\n",
2246 em_settextex_supported ? "" : "NOT ");
2248 expect_empty(hwnd, WM_GETTEXT);
2249 expect_empty(hwnd, EM_GETTEXTEX);
2251 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
2252 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2253 expect_textA(hwnd, WM_GETTEXT, "t");
2254 expect_textA(hwnd, EM_GETTEXTEX, "t");
2255 expect_textW(hwnd, EM_GETTEXTEX, tW);
2257 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
2258 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
2259 expect_textA(hwnd, WM_GETTEXT, "te");
2260 expect_textA(hwnd, EM_GETTEXTEX, "te");
2261 expect_textW(hwnd, EM_GETTEXTEX, teW);
2263 set_textA(hwnd, WM_SETTEXT, NULL);
2264 expect_empty(hwnd, WM_GETTEXT);
2265 expect_empty(hwnd, EM_GETTEXTEX);
2267 if (is_win9x)
2268 set_textA(hwnd, WM_SETTEXT, textW);
2269 else
2270 set_textA(hwnd, WM_SETTEXT, textA);
2271 expect_textA(hwnd, WM_GETTEXT, textA);
2272 expect_textA(hwnd, EM_GETTEXTEX, textA);
2273 expect_textW(hwnd, EM_GETTEXTEX, textW);
2275 if (em_settextex_supported)
2277 set_textA(hwnd, EM_SETTEXTEX, textA);
2278 expect_textA(hwnd, WM_GETTEXT, textA);
2279 expect_textA(hwnd, EM_GETTEXTEX, textA);
2280 expect_textW(hwnd, EM_GETTEXTEX, textW);
2283 if (!is_win9x)
2285 set_textW(hwnd, WM_SETTEXT, textW);
2286 expect_textW(hwnd, WM_GETTEXT, textW);
2287 expect_textA(hwnd, WM_GETTEXT, textA);
2288 expect_textW(hwnd, EM_GETTEXTEX, textW);
2289 expect_textA(hwnd, EM_GETTEXTEX, textA);
2291 if (em_settextex_supported)
2293 set_textW(hwnd, EM_SETTEXTEX, textW);
2294 expect_textW(hwnd, WM_GETTEXT, textW);
2295 expect_textA(hwnd, WM_GETTEXT, textA);
2296 expect_textW(hwnd, EM_GETTEXTEX, textW);
2297 expect_textA(hwnd, EM_GETTEXTEX, textA);
2300 DestroyWindow(hwnd);
2302 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
2303 0, 0, 200, 60, 0, 0, 0, 0);
2304 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2306 ret = IsWindowUnicode(hwnd);
2307 ok(!ret, "RichEdit20A should NOT be unicode\n");
2309 set_textA(hwnd, WM_SETTEXT, textA);
2310 expect_textA(hwnd, WM_GETTEXT, textA);
2311 expect_textA(hwnd, EM_GETTEXTEX, textA);
2312 expect_textW(hwnd, EM_GETTEXTEX, textW);
2314 if (em_settextex_supported)
2316 set_textA(hwnd, EM_SETTEXTEX, textA);
2317 expect_textA(hwnd, WM_GETTEXT, textA);
2318 expect_textA(hwnd, EM_GETTEXTEX, textA);
2319 expect_textW(hwnd, EM_GETTEXTEX, textW);
2322 if (!is_win9x)
2324 set_textW(hwnd, WM_SETTEXT, textW);
2325 expect_textW(hwnd, WM_GETTEXT, textW);
2326 expect_textA(hwnd, WM_GETTEXT, textA);
2327 expect_textW(hwnd, EM_GETTEXTEX, textW);
2328 expect_textA(hwnd, EM_GETTEXTEX, textA);
2330 if (em_settextex_supported)
2332 set_textW(hwnd, EM_SETTEXTEX, textW);
2333 expect_textW(hwnd, WM_GETTEXT, textW);
2334 expect_textA(hwnd, WM_GETTEXT, textA);
2335 expect_textW(hwnd, EM_GETTEXTEX, textW);
2336 expect_textA(hwnd, EM_GETTEXTEX, textA);
2339 DestroyWindow(hwnd);
2342 static void test_WM_CHAR(void)
2344 HWND hwnd;
2345 int ret;
2346 const char * char_list = "abc\rabc\r";
2347 const char * expected_content_single = "abcabc";
2348 const char * expected_content_multi = "abc\r\nabc\r\n";
2349 char buffer[64] = {0};
2350 const char * p;
2352 /* single-line control must IGNORE carriage returns */
2353 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2354 0, 0, 200, 60, 0, 0, 0, 0);
2355 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2357 p = char_list;
2358 while (*p != '\0') {
2359 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2360 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2361 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2362 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2363 p++;
2366 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2367 ret = strcmp(buffer, expected_content_single);
2368 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2370 DestroyWindow(hwnd);
2372 /* multi-line control inserts CR normally */
2373 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
2374 0, 0, 200, 60, 0, 0, 0, 0);
2375 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2377 p = char_list;
2378 while (*p != '\0') {
2379 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
2380 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
2381 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
2382 SendMessageA(hwnd, WM_KEYUP, *p, 1);
2383 p++;
2386 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2387 ret = strcmp(buffer, expected_content_multi);
2388 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2390 DestroyWindow(hwnd);
2393 static void test_EM_GETTEXTLENGTHEX(void)
2395 HWND hwnd;
2396 GETTEXTLENGTHEX gtl;
2397 int ret;
2398 const char * test_string = "a\nb\n\n\r\n";
2399 const char * test_string_after = "a";
2400 char buffer[64] = {0};
2402 /* single line */
2403 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
2404 0, 0, 200, 60, 0, 0, 0, 0);
2405 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2407 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2408 gtl.codepage = CP_ACP;
2409 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2410 ok(ret == 0, "ret %d\n",ret);
2412 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2413 gtl.codepage = CP_ACP;
2414 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2415 ok(ret == 0, "ret %d\n",ret);
2417 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
2419 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2420 gtl.codepage = CP_ACP;
2421 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2422 ok(ret == 1, "ret %d\n",ret);
2424 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2425 gtl.codepage = CP_ACP;
2426 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2427 ok(ret == 1, "ret %d\n",ret);
2429 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2430 ret = strcmp(buffer, test_string_after);
2431 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
2433 DestroyWindow(hwnd);
2435 /* multi line */
2436 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
2437 0, 0, 200, 60, 0, 0, 0, 0);
2438 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
2440 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2441 gtl.codepage = CP_ACP;
2442 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2443 todo_wine ok(ret == 0, "ret %d\n",ret);
2445 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2446 gtl.codepage = CP_ACP;
2447 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2448 ok(ret == 0, "ret %d\n",ret);
2450 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
2452 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
2453 gtl.codepage = CP_ACP;
2454 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2455 todo_wine ok(ret == 10, "ret %d\n",ret);
2457 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
2458 gtl.codepage = CP_ACP;
2459 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
2460 ok(ret == 6, "ret %d\n",ret);
2462 DestroyWindow(hwnd);
2466 /* globals that parent and child access when checking event masks & notifications */
2467 static HWND eventMaskEditHwnd = 0;
2468 static int queriedEventMask;
2469 static int watchForEventMask = 0;
2471 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
2472 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2474 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
2476 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2478 return DefWindowProcA(hwnd, message, wParam, lParam);
2481 /* test event masks in combination with WM_COMMAND */
2482 static void test_eventMask(void)
2484 HWND parent;
2485 int ret;
2486 WNDCLASSA cls;
2487 const char text[] = "foo bar\n";
2488 int eventMask;
2490 /* register class to capture WM_COMMAND */
2491 cls.style = 0;
2492 cls.lpfnWndProc = ParentMsgCheckProcA;
2493 cls.cbClsExtra = 0;
2494 cls.cbWndExtra = 0;
2495 cls.hInstance = GetModuleHandleA(0);
2496 cls.hIcon = 0;
2497 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
2498 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
2499 cls.lpszMenuName = NULL;
2500 cls.lpszClassName = "EventMaskParentClass";
2501 if(!RegisterClassA(&cls)) assert(0);
2503 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
2504 0, 0, 200, 60, NULL, NULL, NULL, NULL);
2505 ok (parent != 0, "Failed to create parent window\n");
2507 eventMaskEditHwnd = new_richedit(parent);
2508 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
2510 eventMask = ENM_CHANGE | ENM_UPDATE;
2511 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
2512 ok(ret == ENM_NONE, "wrong event mask\n");
2513 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
2514 ok(ret == eventMask, "failed to set event mask\n");
2516 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
2517 queriedEventMask = 0; /* initialize to something other than we expect */
2518 watchForEventMask = EN_CHANGE;
2519 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
2520 ok(ret == TRUE, "failed to set text\n");
2521 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
2522 notification in response to WM_SETTEXT */
2523 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
2524 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
2529 START_TEST( editor )
2531 MSG msg;
2532 time_t end;
2534 /* Must explicitly LoadLibrary(). The test has no references to functions in
2535 * RICHED20.DLL, so the linker doesn't actually link to it. */
2536 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
2537 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
2539 test_WM_CHAR();
2540 test_EM_FINDTEXT();
2541 test_EM_GETLINE();
2542 test_EM_SCROLLCARET();
2543 test_EM_SCROLL();
2544 test_WM_SETTEXT();
2545 test_EM_SETCHARFORMAT();
2546 test_EM_SETTEXTMODE();
2547 test_TM_PLAINTEXT();
2548 test_EM_SETOPTIONS();
2549 test_WM_GETTEXT();
2550 test_EM_AUTOURLDETECT();
2551 test_EM_SETUNDOLIMIT();
2552 test_ES_PASSWORD();
2553 test_EM_SETTEXTEX();
2554 test_EM_LIMITTEXT();
2555 test_EM_EXLIMITTEXT();
2556 test_EM_GETLIMITTEXT();
2557 test_WM_SETFONT();
2558 test_EM_GETMODIFY();
2559 test_EM_EXSETSEL();
2560 test_WM_PASTE();
2561 test_EM_StreamIn_Undo();
2562 test_EM_FORMATRANGE();
2563 test_unicode_conversions();
2564 test_EM_GETTEXTLENGTHEX();
2565 test_EM_REPLACESEL();
2566 test_eventMask();
2568 /* Set the environment variable WINETEST_RICHED20 to keep windows
2569 * responsive and open for 30 seconds. This is useful for debugging.
2571 * The message pump uses PeekMessage() to empty the queue and then sleeps for
2572 * 50ms before retrying the queue. */
2573 end = time(NULL) + 30;
2574 if (getenv( "WINETEST_RICHED20" )) {
2575 while (time(NULL) < end) {
2576 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
2577 TranslateMessage(&msg);
2578 DispatchMessage(&msg);
2579 } else {
2580 Sleep(50);
2585 OleFlushClipboard();
2586 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());