riched20: Fix text replacement with text limit.
[wine/testsucceed.git] / dlls / riched20 / tests / editor.c
blob4f95bfe703a5ddfb54e9b88dc79e920cf9f89bd0
1 /*
2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <wine/test.h>
23 #include <windows.h>
24 #include <richedit.h>
25 #include <time.h>
26 #include <stdio.h>
28 static HMODULE hmoduleRichEdit;
30 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
31 HWND hwnd;
32 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
33 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
34 hmoduleRichEdit, NULL);
35 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
36 return hwnd;
39 static HWND new_richedit(HWND parent) {
40 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
43 static const char haystack[] = "WINEWine wineWine wine WineWine";
44 /* ^0 ^10 ^20 ^30 */
46 struct find_s {
47 int start;
48 int end;
49 const char *needle;
50 int flags;
51 int expected_loc;
52 int _todo_wine;
56 struct find_s find_tests[] = {
57 /* Find in empty text */
58 {0, -1, "foo", FR_DOWN, -1, 0},
59 {0, -1, "foo", 0, -1, 0},
60 {0, -1, "", FR_DOWN, -1, 0},
61 {20, 5, "foo", FR_DOWN, -1, 0},
62 {5, 20, "foo", FR_DOWN, -1, 0}
65 struct find_s find_tests2[] = {
66 /* No-result find */
67 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
68 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
70 /* Subsequent finds */
71 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
72 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
73 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
74 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
76 /* Find backwards */
77 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
78 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
79 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
81 /* Case-insensitive */
82 {1, 31, "wInE", FR_DOWN, 4, 0},
83 {1, 31, "Wine", FR_DOWN, 4, 0},
85 /* High-to-low ranges */
86 {20, 5, "Wine", FR_DOWN, -1, 0},
87 {2, 1, "Wine", FR_DOWN, -1, 0},
88 {30, 29, "Wine", FR_DOWN, -1, 0},
89 {20, 5, "Wine", 0, 13, 0},
91 /* Find nothing */
92 {5, 10, "", FR_DOWN, -1, 0},
93 {10, 5, "", FR_DOWN, -1, 0},
94 {0, -1, "", FR_DOWN, -1, 0},
95 {10, 5, "", 0, -1, 0},
97 /* Whole-word search */
98 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
99 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
100 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
101 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
102 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
103 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
104 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
106 /* Bad ranges */
107 {5, 200, "XXX", FR_DOWN, -1, 0},
108 {-20, 20, "Wine", FR_DOWN, -1, 0},
109 {-20, 20, "Wine", FR_DOWN, -1, 0},
110 {-15, -20, "Wine", FR_DOWN, -1, 0},
111 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
113 /* Check the case noted in bug 4479 where matches at end aren't recognized */
114 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
115 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
116 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
117 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
118 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
120 /* The backwards case of bug 4479; bounds look right
121 * Fails because backward find is wrong */
122 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
123 {0, 20, "WINE", FR_MATCHCASE, -1, 0}
126 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
127 int findloc;
128 FINDTEXT ft;
129 memset(&ft, 0, sizeof(ft));
130 ft.chrg.cpMin = f->start;
131 ft.chrg.cpMax = f->end;
132 ft.lpstrText = f->needle;
133 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
134 ok(findloc == f->expected_loc,
135 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
136 name, id, f->needle, f->start, f->end, f->flags, findloc);
139 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
140 int id) {
141 int findloc;
142 FINDTEXTEX ft;
143 memset(&ft, 0, sizeof(ft));
144 ft.chrg.cpMin = f->start;
145 ft.chrg.cpMax = f->end;
146 ft.lpstrText = f->needle;
147 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
148 ok(findloc == f->expected_loc,
149 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
150 name, id, f->needle, f->start, f->end, f->flags, findloc);
151 ok(ft.chrgText.cpMin == f->expected_loc,
152 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
153 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
154 ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
155 : f->expected_loc + strlen(f->needle)),
156 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n",
157 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
160 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
161 int num_tests)
163 int i;
165 for (i = 0; i < num_tests; i++) {
166 if (find[i]._todo_wine) {
167 todo_wine {
168 check_EM_FINDTEXT(hwnd, name, &find[i], i);
169 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
171 } else {
172 check_EM_FINDTEXT(hwnd, name, &find[i], i);
173 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
178 static void test_EM_FINDTEXT(void)
180 HWND hwndRichEdit = new_richedit(NULL);
182 /* Empty rich edit control */
183 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
184 sizeof(find_tests)/sizeof(struct find_s));
186 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
188 /* Haystack text */
189 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
190 sizeof(find_tests2)/sizeof(struct find_s));
192 DestroyWindow(hwndRichEdit);
195 static const struct getline_s {
196 int line;
197 size_t buffer_len;
198 const char *text;
199 } gl[] = {
200 {0, 10, "foo bar\r"},
201 {1, 10, "\r"},
202 {2, 10, "bar\r"},
203 {3, 10, "\r"},
205 /* Buffer smaller than line length */
206 {0, 2, "foo bar\r"},
207 {0, 1, "foo bar\r"},
208 {0, 0, "foo bar\r"}
211 static void test_EM_GETLINE(void)
213 int i;
214 HWND hwndRichEdit = new_richedit(NULL);
215 static const int nBuf = 1024;
216 char dest[1024], origdest[1024];
217 const char text[] = "foo bar\n"
218 "\n"
219 "bar\n";
221 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
223 memset(origdest, 0xBB, nBuf);
224 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
226 int nCopied;
227 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
228 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
229 memset(dest, 0xBB, nBuf);
230 *(WORD *) dest = gl[i].buffer_len;
232 /* EM_GETLINE appends a "\r\0" to the end of the line
233 * nCopied counts up to and including the '\r' */
234 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
235 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
236 expected_nCopied);
237 /* two special cases since a parameter is passed via dest */
238 if (gl[i].buffer_len == 0)
239 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
240 "buffer_len=0\n");
241 else if (gl[i].buffer_len == 1)
242 ok(dest[0] == gl[i].text[0] && !dest[1] &&
243 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
244 else
246 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
247 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
248 ok(!strncmp(dest + expected_bytes_written, origdest
249 + expected_bytes_written, nBuf - expected_bytes_written),
250 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
254 DestroyWindow(hwndRichEdit);
257 static int get_scroll_pos_y(HWND hwnd)
259 POINT p = {-1, -1};
260 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
261 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
262 return p.y;
265 static void move_cursor(HWND hwnd, long charindex)
267 CHARRANGE cr;
268 cr.cpMax = charindex;
269 cr.cpMin = charindex;
270 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
273 static void line_scroll(HWND hwnd, int amount)
275 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
278 static void test_EM_SCROLLCARET(void)
280 int prevY, curY;
281 HWND hwndRichEdit = new_richedit(NULL);
282 const char text[] = "aa\n"
283 "this is a long line of text that should be longer than the "
284 "control's width\n"
285 "cc\n"
286 "dd\n"
287 "ee\n"
288 "ff\n"
289 "gg\n"
290 "hh\n";
292 /* Can't verify this */
293 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
295 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
297 /* Caret above visible window */
298 line_scroll(hwndRichEdit, 3);
299 prevY = get_scroll_pos_y(hwndRichEdit);
300 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
301 curY = get_scroll_pos_y(hwndRichEdit);
302 ok(prevY != curY, "%d == %d\n", prevY, curY);
304 /* Caret below visible window */
305 move_cursor(hwndRichEdit, sizeof(text) - 1);
306 line_scroll(hwndRichEdit, -3);
307 prevY = get_scroll_pos_y(hwndRichEdit);
308 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
309 curY = get_scroll_pos_y(hwndRichEdit);
310 ok(prevY != curY, "%d == %d\n", prevY, curY);
312 /* Caret in visible window */
313 move_cursor(hwndRichEdit, sizeof(text) - 2);
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 still in visible window */
320 line_scroll(hwndRichEdit, -1);
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 DestroyWindow(hwndRichEdit);
329 static void test_EM_SETTEXTMODE(void)
331 HWND hwndRichEdit = new_richedit(NULL);
332 CHARFORMAT2 cf2, cf2test;
333 CHARRANGE cr;
334 int rc = 0;
336 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
337 /*Insert text into the control*/
339 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
341 /*Attempt to change the control to plain text mode*/
342 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
343 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
345 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
346 If rich text is pasted, it should have the same formatting as the rest
347 of the text in the control*/
349 /*Italicize the text
350 *NOTE: If the default text was already italicized, the test will simply
351 reverse; in other words, it will copy a regular "wine" into a plain
352 text window that uses an italicized format*/
353 cf2.cbSize = sizeof(CHARFORMAT2);
354 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
355 (LPARAM) &cf2);
357 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
358 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
360 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
361 however, SCF_ALL has been implemented*/
362 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
363 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
365 /*Select the string "wine"*/
366 cr.cpMin = 0;
367 cr.cpMax = 4;
368 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
370 /*Copy the italicized "wine" to the clipboard*/
371 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
373 /*Reset the formatting to default*/
374 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
375 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
377 /*Clear the text in the control*/
378 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
380 /*Switch to Plain Text Mode*/
381 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
382 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
384 /*Input "wine" again in normal format*/
385 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
387 /*Paste the italicized "wine" into the control*/
388 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
390 /*Select a character from the first "wine" string*/
391 cr.cpMin = 2;
392 cr.cpMax = 3;
393 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
395 /*Retrieve its formatting*/
396 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
397 (LPARAM) &cf2);
399 /*Select a character from the second "wine" string*/
400 cr.cpMin = 5;
401 cr.cpMax = 6;
402 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
404 /*Retrieve its formatting*/
405 cf2test.cbSize = sizeof(CHARFORMAT2);
406 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
407 (LPARAM) &cf2test);
409 /*Compare the two formattings*/
410 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
411 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
412 cf2.dwEffects, cf2test.dwEffects);
413 /*Test TM_RICHTEXT by: switching back to Rich Text mode
414 printing "wine" in the current format(normal)
415 pasting "wine" from the clipboard(italicized)
416 comparing the two formats(should differ)*/
418 /*Attempt to switch with text in control*/
419 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
420 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
422 /*Clear control*/
423 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
425 /*Switch into Rich Text mode*/
426 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
427 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
429 /*Print "wine" in normal formatting into the control*/
430 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
432 /*Paste italicized "wine" into the control*/
433 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
435 /*Select text from the first "wine" string*/
436 cr.cpMin = 1;
437 cr.cpMax = 3;
438 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
440 /*Retrieve its formatting*/
441 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
442 (LPARAM) &cf2);
444 /*Select text from the second "wine" string*/
445 cr.cpMin = 6;
446 cr.cpMax = 7;
447 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
449 /*Retrieve its formatting*/
450 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
451 (LPARAM) &cf2test);
453 /*Test that the two formattings are not the same*/
454 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
455 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
456 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
458 DestroyWindow(hwndRichEdit);
461 static void test_TM_PLAINTEXT(void)
463 /*Tests plain text properties*/
465 HWND hwndRichEdit = new_richedit(NULL);
466 CHARFORMAT2 cf2, cf2test;
467 CHARRANGE cr;
469 /*Switch to plain text mode*/
471 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
472 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
474 /*Fill control with text*/
476 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
478 /*Select some text and bold it*/
480 cr.cpMin = 10;
481 cr.cpMax = 20;
482 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
483 cf2.cbSize = sizeof(CHARFORMAT2);
484 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
485 (LPARAM) &cf2);
487 cf2.dwMask = CFM_BOLD | cf2.dwMask;
488 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
490 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
492 /*Get the formatting of those characters*/
494 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
496 /*Get the formatting of some other characters*/
497 cf2test.cbSize = sizeof(CHARFORMAT2);
498 cr.cpMin = 21;
499 cr.cpMax = 30;
500 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
501 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
503 /*Test that they are the same as plain text allows only one formatting*/
505 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
506 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
507 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
509 /*Fill the control with a "wine" string, which when inserted will be bold*/
511 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
513 /*Copy the bolded "wine" string*/
515 cr.cpMin = 0;
516 cr.cpMax = 4;
517 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
518 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
520 /*Swap back to rich text*/
522 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
523 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
525 /*Set the default formatting to bold italics*/
527 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
528 cf2.dwMask |= CFM_ITALIC;
529 cf2.dwEffects ^= CFE_ITALIC;
530 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
532 /*Set the text in the control to "wine", which will be bold and italicized*/
534 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
536 /*Paste the plain text "wine" string, which should take the insert
537 formatting, which at the moment is bold italics*/
539 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
541 /*Select the first "wine" string and retrieve its formatting*/
543 cr.cpMin = 1;
544 cr.cpMax = 3;
545 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
546 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
548 /*Select the second "wine" string and retrieve its formatting*/
550 cr.cpMin = 5;
551 cr.cpMax = 7;
552 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
553 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
555 /*Compare the two formattings. They should be the same.*/
557 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
558 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
559 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
560 DestroyWindow(hwndRichEdit);
563 static void test_WM_GETTEXT(void)
565 HWND hwndRichEdit = new_richedit(NULL);
566 static const char text[] = "Hello. My name is RichEdit!";
567 char buffer[1024] = {0};
568 int result;
570 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
571 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
572 result = strcmp(buffer,text);
573 ok(result == 0,
574 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
575 DestroyWindow(hwndRichEdit);
578 /* FIXME: need to test unimplemented options and robustly test wparam */
579 static void test_EM_SETOPTIONS(void)
581 HWND hwndRichEdit = new_richedit(NULL);
582 static const char text[] = "Hello. My name is RichEdit!";
583 char buffer[1024] = {0};
585 /* NEGATIVE TESTING - NO OPTIONS SET */
586 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
587 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
589 /* testing no readonly by sending 'a' to the control*/
590 SetFocus(hwndRichEdit);
591 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
592 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
593 ok(buffer[0]=='a',
594 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
595 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
597 /* READONLY - sending 'a' to the control */
598 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
599 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
600 SetFocus(hwndRichEdit);
601 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
602 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
603 ok(buffer[0]==text[0],
604 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
606 DestroyWindow(hwndRichEdit);
609 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
611 CHARFORMAT2W text_format;
612 int link_present = 0;
613 text_format.cbSize = sizeof(text_format);
614 SendMessage(hwnd, EM_SETSEL, 0, 0);
615 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
616 link_present = text_format.dwEffects & CFE_LINK;
617 if (is_url)
618 { /* control text is url; should get CFE_LINK */
619 ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
621 else
623 ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
627 static HWND new_static_wnd(HWND parent) {
628 return new_window("Static", 0, parent);
631 static void test_EM_AUTOURLDETECT(void)
633 struct urls_s {
634 const char *text;
635 int is_url;
636 } urls[12] = {
637 {"winehq.org", 0},
638 {"http://www.winehq.org", 1},
639 {"http//winehq.org", 0},
640 {"ww.winehq.org", 0},
641 {"www.winehq.org", 1},
642 {"ftp://192.168.1.1", 1},
643 {"ftp//192.168.1.1", 0},
644 {"mailto:your@email.com", 1},
645 {"prospero:prosperoserver", 1},
646 {"telnet:test", 1},
647 {"news:newserver", 1},
648 {"wais:waisserver", 1}
651 int i;
652 int urlRet=-1;
653 HWND hwndRichEdit, parent;
655 parent = new_static_wnd(NULL);
656 hwndRichEdit = new_richedit(parent);
657 /* Try and pass EM_AUTOURLDETECT some test wParam values */
658 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
659 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
660 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
661 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
662 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
663 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
664 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
665 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
666 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
667 /* for each url, check the text to see if CFE_LINK effect is present */
668 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
669 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
670 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
671 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
672 check_CFE_LINK_rcvd(hwndRichEdit, 0);
673 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
674 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
675 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
676 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
678 DestroyWindow(hwndRichEdit);
679 DestroyWindow(parent);
682 static void test_EM_SCROLL(void)
684 int i, j;
685 int r; /* return value */
686 int expr; /* expected return value */
687 HWND hwndRichEdit = new_richedit(NULL);
688 int y_before, y_after; /* units of lines of text */
690 /* test a richedit box containing a single line of text */
691 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
692 expr = 0x00010000;
693 for (i = 0; i < 4; i++) {
694 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
696 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
697 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
698 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
699 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
700 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
701 "(i == %d)\n", y_after, i);
705 * test a richedit box that will scroll. There are two general
706 * cases: the case without any long lines and the case with a long
707 * line.
709 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
710 if (i == 0)
711 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
712 else
713 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
714 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
715 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
716 "LONG LINE \nb\nc\nd\ne");
717 for (j = 0; j < 12; j++) /* reset scrol position to top */
718 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
720 /* get first visible line */
721 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
722 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
724 /* get new current first visible line */
725 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
727 ok(((r & 0xffffff00) == 0x00010000) &&
728 ((r & 0x000000ff) != 0x00000000),
729 "EM_SCROLL page down didn't scroll by a small positive number of "
730 "lines (r == 0x%08x)\n", r);
731 ok(y_after > y_before, "EM_SCROLL page down not functioning "
732 "(line %d scrolled to line %d\n", y_before, y_after);
734 y_before = y_after;
736 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
737 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
738 ok(((r & 0xffffff00) == 0x0001ff00),
739 "EM_SCROLL page up didn't scroll by a small negative number of lines "
740 "(r == 0x%08x)\n", r);
741 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
742 "%d scrolled to line %d\n", y_before, y_after);
744 y_before = y_after;
746 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
748 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
750 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
751 "(r == 0x%08x)\n", r);
752 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
753 "1 line (%d scrolled to %d)\n", y_before, y_after);
755 y_before = y_after;
757 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
759 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
761 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
762 "(r == 0x%08x)\n", r);
763 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
764 "line (%d scrolled to %d)\n", y_before, y_after);
766 y_before = y_after;
768 r = SendMessage(hwndRichEdit, EM_SCROLL,
769 SB_LINEUP, 0); /* lineup beyond top */
771 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
773 ok(r == 0x00010000,
774 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
775 ok(y_before == y_after,
776 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
778 y_before = y_after;
780 r = SendMessage(hwndRichEdit, EM_SCROLL,
781 SB_PAGEUP, 0);/*page up beyond top */
783 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
785 ok(r == 0x00010000,
786 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
787 ok(y_before == y_after,
788 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
790 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
791 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
792 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
793 r = SendMessage(hwndRichEdit, EM_SCROLL,
794 SB_PAGEDOWN, 0); /* page down beyond bot */
795 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
797 ok(r == 0x00010000,
798 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
799 ok(y_before == y_after,
800 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
801 y_before, y_after);
803 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
804 SendMessage(hwndRichEdit, EM_SCROLL,
805 SB_LINEDOWN, 0); /* line down beyond bot */
806 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
808 ok(r == 0x00010000,
809 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
810 ok(y_before == y_after,
811 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
812 y_before, y_after);
814 DestroyWindow(hwndRichEdit);
817 static void test_EM_SETUNDOLIMIT(void)
819 /* cases we test for:
820 * default behaviour - limiting at 100 undo's
821 * undo disabled - setting a limit of 0
822 * undo limited - undo limit set to some to some number, like 2
823 * bad input - sending a negative number should default to 100 undo's */
825 HWND hwndRichEdit = new_richedit(NULL);
826 CHARRANGE cr;
827 int i;
828 int result;
830 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
831 cr.cpMin = 0;
832 cr.cpMax = 1;
833 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
834 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
835 also, multiple pastes don't combine like WM_CHAR would */
836 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
838 /* first case - check the default */
839 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
840 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
841 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
842 for (i=0; i<100; i++) /* Undo 100 of them */
843 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
844 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
845 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
847 /* second case - cannot undo */
848 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
849 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
850 SendMessage(hwndRichEdit,
851 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
852 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
853 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
855 /* third case - set it to an arbitrary number */
856 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
857 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
858 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
859 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
860 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
861 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
862 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
863 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
864 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
865 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
866 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
867 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
868 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
869 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
871 /* fourth case - setting negative numbers should default to 100 undos */
872 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
873 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
874 ok (result == 100,
875 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
877 DestroyWindow(hwndRichEdit);
880 static void test_ES_PASSWORD(void)
882 /* This isn't hugely testable, so we're just going to run it through it's paces. */
884 HWND hwndRichEdit = new_richedit(NULL);
885 WCHAR result;
887 /* First, check the default of a regular control */
888 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
889 ok (result == 0,
890 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
892 /* Now, set it to something normal */
893 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
894 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
895 ok (result == 120,
896 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
898 /* Now, set it to something odd */
899 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
900 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
901 ok (result == 1234,
902 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
903 DestroyWindow(hwndRichEdit);
906 static void test_EM_SETTEXTEX(void)
908 HWND hwndRichEdit = new_richedit(NULL);
909 SETTEXTEX setText;
910 GETTEXTEX getText;
911 WCHAR TestItem1[] = {'T', 'e', 's', 't',
912 'S', 'o', 'm', 'e',
913 'T', 'e', 'x', 't', 0};
914 #define MAX_BUF_LEN 1024
915 WCHAR buf[MAX_BUF_LEN];
916 int result;
917 CHARRANGE cr;
919 setText.codepage = 1200; /* no constant for unicode */
920 getText.codepage = 1200; /* no constant for unicode */
921 getText.cb = MAX_BUF_LEN;
922 getText.flags = GT_DEFAULT;
924 setText.flags = 0;
925 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
926 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
927 ok(lstrcmpW(buf, TestItem1) == 0,
928 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
930 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
931 (WPARAM)&setText, (LPARAM) NULL);
932 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
934 ok (result == 1,
935 "EM_SETTEXTEX returned %d, instead of 1\n",result);
936 ok(lstrlenW(buf) == 0,
937 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
939 /* put some text back */
940 setText.flags = 0;
941 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
942 /* select some text */
943 cr.cpMax = 1;
944 cr.cpMin = 3;
945 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
946 /* replace current selection */
947 setText.flags = ST_SELECTION;
948 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
949 (WPARAM)&setText, (LPARAM) NULL);
950 ok(result == 0,
951 "EM_SETTEXTEX with NULL lParam to replace selection"
952 " with no text should return 0. Got %i\n",
953 result);
955 /* put some text back */
956 setText.flags = 0;
957 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
958 /* select some text */
959 cr.cpMax = 1;
960 cr.cpMin = 3;
961 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
962 /* replace current selection */
963 setText.flags = ST_SELECTION;
964 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
965 (WPARAM)&setText, (LPARAM) TestItem1);
966 /* get text */
967 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
968 ok(result == lstrlenW(TestItem1),
969 "EM_SETTEXTEX with NULL lParam to replace selection"
970 " with no text should return 0. Got %i\n",
971 result);
972 ok(lstrlenW(buf) == 22,
973 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
974 lstrlenW(buf) );
976 DestroyWindow(hwndRichEdit);
979 static void test_EM_LIMITTEXT(void)
981 int ret;
983 HWND hwndRichEdit = new_richedit(NULL);
985 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
986 * about setting the length to -1 for multiline edit controls doesn't happen.
989 /* Don't check default gettextlimit case. That's done in other tests */
991 /* Set textlimit to 100 */
992 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
993 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
994 ok (ret == 100,
995 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
997 /* Set textlimit to 0 */
998 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
999 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1000 ok (ret == 65536,
1001 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1003 /* Set textlimit to -1 */
1004 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1005 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1006 ok (ret == -1,
1007 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1009 /* Set textlimit to -2 */
1010 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1011 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1012 ok (ret == -2,
1013 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1015 DestroyWindow (hwndRichEdit);
1019 static void test_EM_EXLIMITTEXT(void)
1021 int i, selBegin, selEnd, len1, len2;
1022 int result;
1023 char text[1024 + 1];
1024 char buffer[1024 + 1];
1025 int textlimit = 0; /* multiple of 100 */
1026 HWND hwndRichEdit = new_richedit(NULL);
1028 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1029 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1031 textlimit = 256000;
1032 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1033 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1034 /* set higher */
1035 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1037 textlimit = 1000;
1038 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1039 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1040 /* set lower */
1041 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1043 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1044 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1045 /* default for WParam = 0 */
1046 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1048 textlimit = sizeof(text)-1;
1049 memset(text, 'W', textlimit);
1050 text[sizeof(text)-1] = 0;
1051 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1052 /* maxed out text */
1053 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1055 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1056 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1057 len1 = selEnd - selBegin;
1059 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1060 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1061 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1062 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1063 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1064 len2 = selEnd - selBegin;
1066 ok(len1 != len2,
1067 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1068 len1,len2,i);
1070 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1071 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1072 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1073 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1074 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1075 len1 = selEnd - selBegin;
1077 ok(len1 != len2,
1078 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1079 len1,len2,i);
1081 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1082 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1083 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1084 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1085 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1086 len2 = selEnd - selBegin;
1088 ok(len1 == len2,
1089 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1090 len1,len2,i);
1092 /* set text up to the limit, select all the text, then add a char */
1093 textlimit = 5;
1094 memset(text, 'W', textlimit);
1095 text[textlimit] = 0;
1096 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1097 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1098 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1099 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1100 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1101 result = strcmp(buffer, "A");
1102 ok(0 == result, "got string = \"%s\"\n", buffer);
1104 DestroyWindow(hwndRichEdit);
1107 static void test_EM_GETLIMITTEXT(void)
1109 int i;
1110 HWND hwndRichEdit = new_richedit(NULL);
1112 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1113 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1115 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1116 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1117 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1119 DestroyWindow(hwndRichEdit);
1122 static void test_WM_SETFONT(void)
1124 /* There is no invalid input or error conditions for this function.
1125 * NULL wParam and lParam just fall back to their default values
1126 * It should be noted that even if you use a gibberish name for your fonts
1127 * here, it will still work because the name is stored. They will display as
1128 * System, but will report their name to be whatever they were created as */
1130 HWND hwndRichEdit = new_richedit(NULL);
1131 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1132 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1133 FF_DONTCARE, "Marlett");
1134 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1135 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1136 FF_DONTCARE, "MS Sans Serif");
1137 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1138 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1139 FF_DONTCARE, "Courier");
1140 LOGFONTA sentLogFont;
1141 CHARFORMAT2A returnedCF2A;
1143 returnedCF2A.cbSize = sizeof(returnedCF2A);
1145 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1146 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1147 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1149 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1150 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1151 "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1152 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1154 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1155 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1156 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1157 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1158 "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1159 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1161 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1162 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1163 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1164 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1165 "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1166 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1168 /* This last test is special since we send in NULL. We clear the variables
1169 * and just compare to "System" instead of the sent in font name. */
1170 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1171 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1172 returnedCF2A.cbSize = sizeof(returnedCF2A);
1174 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1175 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1176 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1177 ok (!strcmp("System",returnedCF2A.szFaceName),
1178 "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1180 DestroyWindow(hwndRichEdit);
1184 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1185 LPBYTE pbBuff,
1186 LONG cb,
1187 LONG *pcb)
1189 const char** str = (const char**)dwCookie;
1190 int size = strlen(*str);
1191 if(size > 3) /* let's make it peice-meal for fun */
1192 size = 3;
1193 *pcb = cb;
1194 if (*pcb > size) {
1195 *pcb = size;
1197 if (*pcb > 0) {
1198 memcpy(pbBuff, *str, *pcb);
1199 *str += *pcb;
1201 return 0;
1204 static void test_EM_GETMODIFY(void)
1206 HWND hwndRichEdit = new_richedit(NULL);
1207 LRESULT result;
1208 SETTEXTEX setText;
1209 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1210 'S', 'o', 'm', 'e',
1211 'T', 'e', 'x', 't', 0};
1212 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1213 'S', 'o', 'm', 'e',
1214 'O', 't', 'h', 'e', 'r',
1215 'T', 'e', 'x', 't', 0};
1216 const char* streamText = "hello world";
1217 CHARFORMAT2 cf2;
1218 PARAFORMAT2 pf2;
1219 EDITSTREAM es;
1221 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1222 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1223 FF_DONTCARE, "Courier");
1225 setText.codepage = 1200; /* no constant for unicode */
1226 setText.flags = ST_KEEPUNDO;
1229 /* modify flag shouldn't be set when richedit is first created */
1230 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1231 ok (result == 0,
1232 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1234 /* setting modify flag should actually set it */
1235 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1236 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1237 ok (result != 0,
1238 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1240 /* clearing modify flag should actually clear it */
1241 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1242 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1243 ok (result == 0,
1244 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1246 /* setting font doesn't change modify flag */
1247 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1248 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1249 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1250 ok (result == 0,
1251 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1253 /* setting text should set modify flag */
1254 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1255 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1256 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1257 ok (result != 0,
1258 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1260 /* undo previous text doesn't reset modify flag */
1261 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1262 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1263 ok (result != 0,
1264 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1266 /* set text with no flag to keep undo stack should not set modify flag */
1267 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1268 setText.flags = 0;
1269 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1270 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1271 ok (result == 0,
1272 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1274 /* WM_SETTEXT doesn't modify */
1275 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1276 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1277 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1278 todo_wine {
1279 ok (result == 0,
1280 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1283 /* clear the text */
1284 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1285 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1286 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1287 ok (result == 0,
1288 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1290 /* replace text */
1291 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1292 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1293 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1294 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1295 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1296 ok (result != 0,
1297 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1299 /* copy/paste text 1 */
1300 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1301 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1302 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1303 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1304 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1305 ok (result != 0,
1306 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1308 /* copy/paste text 2 */
1309 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1310 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1311 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1312 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1313 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1314 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1315 ok (result != 0,
1316 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1318 /* press char */
1319 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1320 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1321 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1322 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1323 ok (result != 0,
1324 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1326 /* set char format */
1327 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1328 cf2.cbSize = sizeof(CHARFORMAT2);
1329 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1330 (LPARAM) &cf2);
1331 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1332 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1333 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1334 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1335 ok (result != 0,
1336 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1338 /* set para format */
1339 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1340 pf2.cbSize = sizeof(PARAFORMAT2);
1341 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1342 (LPARAM) &pf2);
1343 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1344 pf2.wAlignment = PFA_RIGHT;
1345 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1346 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1347 ok (result == 0,
1348 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1350 /* EM_STREAM */
1351 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1352 es.dwCookie = (DWORD_PTR)&streamText;
1353 es.dwError = 0;
1354 es.pfnCallback = test_EM_GETMODIFY_esCallback;
1355 SendMessage(hwndRichEdit, EM_STREAMIN,
1356 (WPARAM)(SF_TEXT), (LPARAM)&es);
1357 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1358 ok (result != 0,
1359 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1361 DestroyWindow(hwndRichEdit);
1364 struct exsetsel_s {
1365 long min;
1366 long max;
1367 long expected_retval;
1368 int expected_getsel_start;
1369 int expected_getsel_end;
1370 int _exsetsel_todo_wine;
1371 int _getsel_todo_wine;
1374 const struct exsetsel_s exsetsel_tests[] = {
1375 /* sanity tests */
1376 {5, 10, 10, 5, 10, 0, 0},
1377 {15, 17, 17, 15, 17, 0, 0},
1378 /* test cpMax > strlen() */
1379 {0, 100, 18, 0, 18, 0, 1},
1380 /* test cpMin == cpMax */
1381 {5, 5, 5, 5, 5, 0, 0},
1382 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1383 {-1, 0, 5, 5, 5, 0, 0},
1384 {-1, 17, 5, 5, 5, 0, 0},
1385 {-1, 18, 5, 5, 5, 0, 0},
1386 /* test cpMin < 0 && cpMax < 0 */
1387 {-1, -1, 17, 17, 17, 0, 0},
1388 {-4, -5, 17, 17, 17, 0, 0},
1389 /* test cMin >=0 && cpMax < 0 (bug 6814) */
1390 {0, -1, 18, 0, 18, 0, 1},
1391 {17, -5, 18, 17, 18, 0, 1},
1392 {18, -3, 17, 17, 17, 0, 0},
1393 /* test if cpMin > cpMax */
1394 {15, 19, 18, 15, 18, 0, 1},
1395 {19, 15, 18, 15, 18, 0, 1}
1398 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1399 CHARRANGE cr;
1400 long result;
1401 int start, end;
1403 cr.cpMin = setsel->min;
1404 cr.cpMax = setsel->max;
1405 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1407 if (setsel->_exsetsel_todo_wine) {
1408 todo_wine {
1409 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1411 } else {
1412 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1415 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1417 if (setsel->_getsel_todo_wine) {
1418 todo_wine {
1419 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);
1421 } else {
1422 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);
1426 static void test_EM_EXSETSEL(void)
1428 HWND hwndRichEdit = new_richedit(NULL);
1429 int i;
1430 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1432 /* sending some text to the window */
1433 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1434 /* 01234567890123456*/
1435 /* 10 */
1437 for (i = 0; i < num_tests; i++) {
1438 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1441 DestroyWindow(hwndRichEdit);
1444 static void test_WM_PASTE(void)
1446 int result;
1447 char buffer[1024] = {0};
1448 const char* text1 = "testing paste\r";
1449 const char* text2 = "testing paste\r\rtesting paste";
1450 const char* text3 = "testing paste\rpaste\rtesting paste";
1451 HWND hwndRichEdit = new_richedit(NULL);
1453 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1454 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1455 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1456 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1457 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1458 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1459 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1460 result = strcmp(text1, buffer);
1461 ok(result == 0,
1462 "test paste: strcmp = %i\n", result);
1464 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1465 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1466 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1467 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1468 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1469 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1470 SendMessage(hwndRichEdit, WM_CHAR, 25, 0); /* ctrl-y */
1471 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1472 result = strcmp(buffer,text3);
1473 ok(result == 0,
1474 "test paste: strcmp = %i\n", result);
1476 DestroyWindow(hwndRichEdit);
1479 static int nCallbackCount = 0;
1481 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1482 LONG cb, LONG* pcb)
1484 const char text[] = {'t','e','s','t'};
1486 if (sizeof(text) <= cb)
1488 if ((int)dwCookie != nCallbackCount)
1490 *pcb = 0;
1491 return 0;
1494 memcpy (pbBuff, text, sizeof(text));
1495 *pcb = sizeof(text);
1497 nCallbackCount++;
1499 return 0;
1501 else
1502 return 1; /* indicates callback failed */
1505 static void test_EM_StreamIn_Undo(void)
1507 /* The purpose of this test is to determine when a EM_StreamIn should be
1508 * undoable. This is important because WM_PASTE currently uses StreamIn and
1509 * pasting should always be undoable but streaming isn't always.
1511 * cases to test:
1512 * StreamIn plain text without SFF_SELECTION.
1513 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1514 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1515 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1516 * Feel free to add tests for other text modes or StreamIn things.
1520 HWND hwndRichEdit = new_richedit(NULL);
1521 LRESULT result;
1522 EDITSTREAM es;
1523 char buffer[1024] = {0};
1524 const char randomtext[] = "Some text";
1526 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1528 /* StreamIn, no SFF_SELECTION */
1529 es.dwCookie = nCallbackCount;
1530 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1531 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1532 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1533 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1534 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1535 result = strcmp (buffer,"test");
1536 ok (result == 0,
1537 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1539 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1540 ok (result == FALSE,
1541 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1543 /* StreamIn, SFF_SELECTION, but nothing selected */
1544 es.dwCookie = nCallbackCount;
1545 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1546 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1547 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1548 SendMessage(hwndRichEdit, EM_STREAMIN,
1549 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1550 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1551 result = strcmp (buffer,"testSome text");
1552 ok (result == 0,
1553 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1555 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1556 ok (result == TRUE,
1557 "EM_STREAMIN with SFF_SELECTION but no selection set "
1558 "should create an undo\n");
1560 /* StreamIn, SFF_SELECTION, with a selection */
1561 es.dwCookie = nCallbackCount;
1562 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1563 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1564 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1565 SendMessage(hwndRichEdit, EM_STREAMIN,
1566 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1567 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1568 result = strcmp (buffer,"Sometesttext");
1569 ok (result == 0,
1570 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1572 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1573 ok (result == TRUE,
1574 "EM_STREAMIN with SFF_SELECTION and selection set "
1575 "should create an undo\n");
1579 static void test_unicode_conversions(void)
1581 static const WCHAR textW[] = {'t','e','s','t',0};
1582 static const char textA[] = "test";
1583 char bufA[64];
1584 WCHAR bufW[64];
1585 HWND hwnd;
1586 int is_win9x, ret;
1588 is_win9x = GetVersion() & 0x80000000;
1590 #define set_textA(hwnd, txt) \
1591 do { \
1592 ret = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)txt); \
1593 ok(ret, "SendMessageA(WM_SETTEXT) error %u\n", GetLastError()); \
1594 } while(0)
1595 #define expect_textA(hwnd, txt) \
1596 do { \
1597 memset(bufA, 0xAA, sizeof(bufA)); \
1598 ret = SendMessageA(hwnd, WM_GETTEXT, 64, (LPARAM)bufA); \
1599 ok(ret, "SendMessageA(WM_GETTEXT) error %u\n", GetLastError()); \
1600 ret = lstrcmpA(bufA, txt); \
1601 ok(!ret, "strings not match: expected %s got %s\n", txt, bufA); \
1602 } while(0)
1604 #define set_textW(hwnd, txt) \
1605 do { \
1606 ret = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)txt); \
1607 ok(ret, "SendMessageW(WM_SETTEXT) error %u\n", GetLastError()); \
1608 } while(0)
1609 #define expect_textW(hwnd, txt) \
1610 do { \
1611 memset(bufW, 0xAA, sizeof(bufW)); \
1612 ret = SendMessageW(hwnd, WM_GETTEXT, 64, (LPARAM)bufW); \
1613 ok(ret, "SendMessageW(WM_GETTEXT) error %u\n", GetLastError()); \
1614 ret = lstrcmpW(bufW, txt); \
1615 ok(!ret, "strings not match expected[0] %x got[0] %x\n", txt[0], bufW[0]); \
1616 } while(0)
1618 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1619 0, 0, 200, 60, 0, 0, 0, 0);
1620 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1622 ret = IsWindowUnicode(hwnd);
1623 if (is_win9x)
1624 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1625 else
1626 ok(ret, "RichEdit20W should be unicode under NT\n");
1628 memset(bufA, 0xAA, sizeof(bufA));
1629 ret = SendMessageA(hwnd, WM_GETTEXT, 64, (LPARAM)bufA);
1630 ok(!ret, "empty richedit should return 0, got %d\n", ret);
1631 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA);
1633 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1634 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1635 expect_textA(hwnd, "t");
1637 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1638 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1639 expect_textA(hwnd, "te");
1641 set_textA(hwnd, NULL);
1642 memset(bufA, 0xAA, sizeof(bufA));
1643 ret = SendMessageA(hwnd, WM_GETTEXT, 64, (LPARAM)bufA);
1644 ok(!ret, "empty richedit should return 0, got %d\n", ret);
1645 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA);
1647 if (is_win9x)
1648 set_textA(hwnd, textW);
1649 else
1650 set_textA(hwnd, textA);
1651 expect_textA(hwnd, textA);
1653 if (!is_win9x)
1655 set_textW(hwnd, textW);
1656 expect_textW(hwnd, textW);
1658 DestroyWindow(hwnd);
1660 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
1661 0, 0, 200, 60, 0, 0, 0, 0);
1662 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1664 ret = IsWindowUnicode(hwnd);
1665 ok(!ret, "RichEdit20A should NOT be unicode\n");
1667 set_textA(hwnd, textA);
1668 expect_textA(hwnd, textA);
1670 if (!is_win9x)
1672 set_textW(hwnd, textW);
1673 expect_textW(hwnd, textW);
1675 DestroyWindow(hwnd);
1678 START_TEST( editor )
1680 MSG msg;
1681 time_t end;
1683 /* Must explicitly LoadLibrary(). The test has no references to functions in
1684 * RICHED20.DLL, so the linker doesn't actually link to it. */
1685 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
1686 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1688 test_EM_FINDTEXT();
1689 test_EM_GETLINE();
1690 test_EM_SCROLLCARET();
1691 test_EM_SCROLL();
1692 test_EM_SETTEXTMODE();
1693 test_TM_PLAINTEXT();
1694 test_EM_SETOPTIONS();
1695 test_WM_GETTEXT();
1696 test_EM_AUTOURLDETECT();
1697 test_EM_SETUNDOLIMIT();
1698 test_ES_PASSWORD();
1699 test_EM_SETTEXTEX();
1700 test_EM_LIMITTEXT();
1701 test_EM_EXLIMITTEXT();
1702 test_EM_GETLIMITTEXT();
1703 test_WM_SETFONT();
1704 test_EM_GETMODIFY();
1705 test_EM_EXSETSEL();
1706 test_WM_PASTE();
1707 test_EM_StreamIn_Undo();
1708 test_unicode_conversions();
1710 /* Set the environment variable WINETEST_RICHED20 to keep windows
1711 * responsive and open for 30 seconds. This is useful for debugging.
1713 * The message pump uses PeekMessage() to empty the queue and then sleeps for
1714 * 50ms before retrying the queue. */
1715 end = time(NULL) + 30;
1716 if (getenv( "WINETEST_RICHED20" )) {
1717 while (time(NULL) < end) {
1718 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1719 TranslateMessage(&msg);
1720 DispatchMessage(&msg);
1721 } else {
1722 Sleep(50);
1727 OleFlushClipboard();
1728 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());