riched20: Add tests for EM_FORMATRANGE.
[wine/multimedia.git] / dlls / riched20 / tests / editor.c
blobb20e719e30c9b3e566d1a089b1b3046d8a6f8e2d
1 /*
2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <assert.h>
25 #include <windef.h>
26 #include <winbase.h>
27 #include <wingdi.h>
28 #include <winuser.h>
29 #include <winnls.h>
30 #include <ole2.h>
31 #include <richedit.h>
32 #include <time.h>
33 #include <wine/test.h>
35 static HMODULE hmoduleRichEdit;
37 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
38 HWND hwnd;
39 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
40 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
41 hmoduleRichEdit, NULL);
42 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
43 return hwnd;
46 static HWND new_richedit(HWND parent) {
47 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
50 static const char haystack[] = "WINEWine wineWine wine WineWine";
51 /* ^0 ^10 ^20 ^30 */
53 struct find_s {
54 int start;
55 int end;
56 const char *needle;
57 int flags;
58 int expected_loc;
59 int _todo_wine;
63 struct find_s find_tests[] = {
64 /* Find in empty text */
65 {0, -1, "foo", FR_DOWN, -1, 0},
66 {0, -1, "foo", 0, -1, 0},
67 {0, -1, "", FR_DOWN, -1, 0},
68 {20, 5, "foo", FR_DOWN, -1, 0},
69 {5, 20, "foo", FR_DOWN, -1, 0}
72 struct find_s find_tests2[] = {
73 /* No-result find */
74 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
75 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
77 /* Subsequent finds */
78 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
79 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
80 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
81 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
83 /* Find backwards */
84 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
85 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
86 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
88 /* Case-insensitive */
89 {1, 31, "wInE", FR_DOWN, 4, 0},
90 {1, 31, "Wine", FR_DOWN, 4, 0},
92 /* High-to-low ranges */
93 {20, 5, "Wine", FR_DOWN, -1, 0},
94 {2, 1, "Wine", FR_DOWN, -1, 0},
95 {30, 29, "Wine", FR_DOWN, -1, 0},
96 {20, 5, "Wine", 0, 13, 0},
98 /* Find nothing */
99 {5, 10, "", FR_DOWN, -1, 0},
100 {10, 5, "", FR_DOWN, -1, 0},
101 {0, -1, "", FR_DOWN, -1, 0},
102 {10, 5, "", 0, -1, 0},
104 /* Whole-word search */
105 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
106 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
107 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
108 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
109 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
110 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
111 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
113 /* Bad ranges */
114 {5, 200, "XXX", FR_DOWN, -1, 0},
115 {-20, 20, "Wine", FR_DOWN, -1, 0},
116 {-20, 20, "Wine", FR_DOWN, -1, 0},
117 {-15, -20, "Wine", FR_DOWN, -1, 0},
118 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
120 /* Check the case noted in bug 4479 where matches at end aren't recognized */
121 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
122 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
123 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
124 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
125 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
127 /* The backwards case of bug 4479; bounds look right
128 * Fails because backward find is wrong */
129 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
130 {0, 20, "WINE", FR_MATCHCASE, -1, 0}
133 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
134 int findloc;
135 FINDTEXT ft;
136 memset(&ft, 0, sizeof(ft));
137 ft.chrg.cpMin = f->start;
138 ft.chrg.cpMax = f->end;
139 ft.lpstrText = f->needle;
140 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
141 ok(findloc == f->expected_loc,
142 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
143 name, id, f->needle, f->start, f->end, f->flags, findloc);
146 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
147 int id) {
148 int findloc;
149 FINDTEXTEX ft;
150 memset(&ft, 0, sizeof(ft));
151 ft.chrg.cpMin = f->start;
152 ft.chrg.cpMax = f->end;
153 ft.lpstrText = f->needle;
154 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
155 ok(findloc == f->expected_loc,
156 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
157 name, id, f->needle, f->start, f->end, f->flags, findloc);
158 ok(ft.chrgText.cpMin == f->expected_loc,
159 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
160 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
161 ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
162 : f->expected_loc + strlen(f->needle)),
163 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n",
164 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
167 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
168 int num_tests)
170 int i;
172 for (i = 0; i < num_tests; i++) {
173 if (find[i]._todo_wine) {
174 todo_wine {
175 check_EM_FINDTEXT(hwnd, name, &find[i], i);
176 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
178 } else {
179 check_EM_FINDTEXT(hwnd, name, &find[i], i);
180 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
185 static void test_EM_FINDTEXT(void)
187 HWND hwndRichEdit = new_richedit(NULL);
189 /* Empty rich edit control */
190 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
191 sizeof(find_tests)/sizeof(struct find_s));
193 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
195 /* Haystack text */
196 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
197 sizeof(find_tests2)/sizeof(struct find_s));
199 DestroyWindow(hwndRichEdit);
202 static const struct getline_s {
203 int line;
204 size_t buffer_len;
205 const char *text;
206 } gl[] = {
207 {0, 10, "foo bar\r"},
208 {1, 10, "\r"},
209 {2, 10, "bar\r"},
210 {3, 10, "\r"},
212 /* Buffer smaller than line length */
213 {0, 2, "foo bar\r"},
214 {0, 1, "foo bar\r"},
215 {0, 0, "foo bar\r"}
218 static void test_EM_GETLINE(void)
220 int i;
221 HWND hwndRichEdit = new_richedit(NULL);
222 static const int nBuf = 1024;
223 char dest[1024], origdest[1024];
224 const char text[] = "foo bar\n"
225 "\n"
226 "bar\n";
228 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
230 memset(origdest, 0xBB, nBuf);
231 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
233 int nCopied;
234 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
235 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
236 memset(dest, 0xBB, nBuf);
237 *(WORD *) dest = gl[i].buffer_len;
239 /* EM_GETLINE appends a "\r\0" to the end of the line
240 * nCopied counts up to and including the '\r' */
241 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
242 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
243 expected_nCopied);
244 /* two special cases since a parameter is passed via dest */
245 if (gl[i].buffer_len == 0)
246 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
247 "buffer_len=0\n");
248 else if (gl[i].buffer_len == 1)
249 ok(dest[0] == gl[i].text[0] && !dest[1] &&
250 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
251 else
253 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
254 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
255 ok(!strncmp(dest + expected_bytes_written, origdest
256 + expected_bytes_written, nBuf - expected_bytes_written),
257 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
261 DestroyWindow(hwndRichEdit);
264 static int get_scroll_pos_y(HWND hwnd)
266 POINT p = {-1, -1};
267 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
268 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
269 return p.y;
272 static void move_cursor(HWND hwnd, long charindex)
274 CHARRANGE cr;
275 cr.cpMax = charindex;
276 cr.cpMin = charindex;
277 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
280 static void line_scroll(HWND hwnd, int amount)
282 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
285 static void test_EM_SCROLLCARET(void)
287 int prevY, curY;
288 HWND hwndRichEdit = new_richedit(NULL);
289 const char text[] = "aa\n"
290 "this is a long line of text that should be longer than the "
291 "control's width\n"
292 "cc\n"
293 "dd\n"
294 "ee\n"
295 "ff\n"
296 "gg\n"
297 "hh\n";
299 /* Can't verify this */
300 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
302 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
304 /* Caret above visible window */
305 line_scroll(hwndRichEdit, 3);
306 prevY = get_scroll_pos_y(hwndRichEdit);
307 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
308 curY = get_scroll_pos_y(hwndRichEdit);
309 ok(prevY != curY, "%d == %d\n", prevY, curY);
311 /* Caret below visible window */
312 move_cursor(hwndRichEdit, sizeof(text) - 1);
313 line_scroll(hwndRichEdit, -3);
314 prevY = get_scroll_pos_y(hwndRichEdit);
315 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
316 curY = get_scroll_pos_y(hwndRichEdit);
317 ok(prevY != curY, "%d == %d\n", prevY, curY);
319 /* Caret in visible window */
320 move_cursor(hwndRichEdit, sizeof(text) - 2);
321 prevY = get_scroll_pos_y(hwndRichEdit);
322 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
323 curY = get_scroll_pos_y(hwndRichEdit);
324 ok(prevY == curY, "%d != %d\n", prevY, curY);
326 /* Caret still in visible window */
327 line_scroll(hwndRichEdit, -1);
328 prevY = get_scroll_pos_y(hwndRichEdit);
329 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
330 curY = get_scroll_pos_y(hwndRichEdit);
331 ok(prevY == curY, "%d != %d\n", prevY, curY);
333 DestroyWindow(hwndRichEdit);
336 static void test_EM_SETTEXTMODE(void)
338 HWND hwndRichEdit = new_richedit(NULL);
339 CHARFORMAT2 cf2, cf2test;
340 CHARRANGE cr;
341 int rc = 0;
343 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
344 /*Insert text into the control*/
346 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
348 /*Attempt to change the control to plain text mode*/
349 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
350 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
352 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
353 If rich text is pasted, it should have the same formatting as the rest
354 of the text in the control*/
356 /*Italicize the text
357 *NOTE: If the default text was already italicized, the test will simply
358 reverse; in other words, it will copy a regular "wine" into a plain
359 text window that uses an italicized format*/
360 cf2.cbSize = sizeof(CHARFORMAT2);
361 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
362 (LPARAM) &cf2);
364 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
365 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
367 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
368 however, SCF_ALL has been implemented*/
369 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
370 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
372 /*Select the string "wine"*/
373 cr.cpMin = 0;
374 cr.cpMax = 4;
375 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
377 /*Copy the italicized "wine" to the clipboard*/
378 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
380 /*Reset the formatting to default*/
381 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
382 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
384 /*Clear the text in the control*/
385 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
387 /*Switch to Plain Text Mode*/
388 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
389 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
391 /*Input "wine" again in normal format*/
392 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
394 /*Paste the italicized "wine" into the control*/
395 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
397 /*Select a character from the first "wine" string*/
398 cr.cpMin = 2;
399 cr.cpMax = 3;
400 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
402 /*Retrieve its formatting*/
403 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
404 (LPARAM) &cf2);
406 /*Select a character from the second "wine" string*/
407 cr.cpMin = 5;
408 cr.cpMax = 6;
409 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
411 /*Retrieve its formatting*/
412 cf2test.cbSize = sizeof(CHARFORMAT2);
413 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
414 (LPARAM) &cf2test);
416 /*Compare the two formattings*/
417 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
418 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
419 cf2.dwEffects, cf2test.dwEffects);
420 /*Test TM_RICHTEXT by: switching back to Rich Text mode
421 printing "wine" in the current format(normal)
422 pasting "wine" from the clipboard(italicized)
423 comparing the two formats(should differ)*/
425 /*Attempt to switch with text in control*/
426 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
427 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
429 /*Clear control*/
430 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
432 /*Switch into Rich Text mode*/
433 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
434 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
436 /*Print "wine" in normal formatting into the control*/
437 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
439 /*Paste italicized "wine" into the control*/
440 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
442 /*Select text from the first "wine" string*/
443 cr.cpMin = 1;
444 cr.cpMax = 3;
445 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
447 /*Retrieve its formatting*/
448 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
449 (LPARAM) &cf2);
451 /*Select text from the second "wine" string*/
452 cr.cpMin = 6;
453 cr.cpMax = 7;
454 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
456 /*Retrieve its formatting*/
457 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
458 (LPARAM) &cf2test);
460 /*Test that the two formattings are not the same*/
461 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
462 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
463 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
465 DestroyWindow(hwndRichEdit);
468 static void test_TM_PLAINTEXT(void)
470 /*Tests plain text properties*/
472 HWND hwndRichEdit = new_richedit(NULL);
473 CHARFORMAT2 cf2, cf2test;
474 CHARRANGE cr;
476 /*Switch to plain text mode*/
478 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
479 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
481 /*Fill control with text*/
483 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
485 /*Select some text and bold it*/
487 cr.cpMin = 10;
488 cr.cpMax = 20;
489 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
490 cf2.cbSize = sizeof(CHARFORMAT2);
491 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
492 (LPARAM) &cf2);
494 cf2.dwMask = CFM_BOLD | cf2.dwMask;
495 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
497 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
499 /*Get the formatting of those characters*/
501 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
503 /*Get the formatting of some other characters*/
504 cf2test.cbSize = sizeof(CHARFORMAT2);
505 cr.cpMin = 21;
506 cr.cpMax = 30;
507 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
508 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
510 /*Test that they are the same as plain text allows only one formatting*/
512 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
513 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
514 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
516 /*Fill the control with a "wine" string, which when inserted will be bold*/
518 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
520 /*Copy the bolded "wine" string*/
522 cr.cpMin = 0;
523 cr.cpMax = 4;
524 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
525 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
527 /*Swap back to rich text*/
529 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
530 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
532 /*Set the default formatting to bold italics*/
534 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
535 cf2.dwMask |= CFM_ITALIC;
536 cf2.dwEffects ^= CFE_ITALIC;
537 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
539 /*Set the text in the control to "wine", which will be bold and italicized*/
541 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
543 /*Paste the plain text "wine" string, which should take the insert
544 formatting, which at the moment is bold italics*/
546 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
548 /*Select the first "wine" string and retrieve its formatting*/
550 cr.cpMin = 1;
551 cr.cpMax = 3;
552 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
553 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
555 /*Select the second "wine" string and retrieve its formatting*/
557 cr.cpMin = 5;
558 cr.cpMax = 7;
559 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
560 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
562 /*Compare the two formattings. They should be the same.*/
564 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
565 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
566 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
567 DestroyWindow(hwndRichEdit);
570 static void test_WM_GETTEXT(void)
572 HWND hwndRichEdit = new_richedit(NULL);
573 static const char text[] = "Hello. My name is RichEdit!";
574 char buffer[1024] = {0};
575 int result;
577 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
578 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
579 result = strcmp(buffer,text);
580 ok(result == 0,
581 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
582 DestroyWindow(hwndRichEdit);
585 /* FIXME: need to test unimplemented options and robustly test wparam */
586 static void test_EM_SETOPTIONS(void)
588 HWND hwndRichEdit = new_richedit(NULL);
589 static const char text[] = "Hello. My name is RichEdit!";
590 char buffer[1024] = {0};
592 /* NEGATIVE TESTING - NO OPTIONS SET */
593 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
594 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
596 /* testing no readonly by sending 'a' to the control*/
597 SetFocus(hwndRichEdit);
598 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
599 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
600 ok(buffer[0]=='a',
601 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
602 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
604 /* READONLY - sending 'a' to the control */
605 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
606 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
607 SetFocus(hwndRichEdit);
608 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
609 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
610 ok(buffer[0]==text[0],
611 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
613 DestroyWindow(hwndRichEdit);
616 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
618 CHARFORMAT2W text_format;
619 int link_present = 0;
620 text_format.cbSize = sizeof(text_format);
621 SendMessage(hwnd, EM_SETSEL, 0, 0);
622 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
623 link_present = text_format.dwEffects & CFE_LINK;
624 if (is_url)
625 { /* control text is url; should get CFE_LINK */
626 ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
628 else
630 ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
634 static HWND new_static_wnd(HWND parent) {
635 return new_window("Static", 0, parent);
638 static void test_EM_AUTOURLDETECT(void)
640 struct urls_s {
641 const char *text;
642 int is_url;
643 } urls[12] = {
644 {"winehq.org", 0},
645 {"http://www.winehq.org", 1},
646 {"http//winehq.org", 0},
647 {"ww.winehq.org", 0},
648 {"www.winehq.org", 1},
649 {"ftp://192.168.1.1", 1},
650 {"ftp//192.168.1.1", 0},
651 {"mailto:your@email.com", 1},
652 {"prospero:prosperoserver", 1},
653 {"telnet:test", 1},
654 {"news:newserver", 1},
655 {"wais:waisserver", 1}
658 int i;
659 int urlRet=-1;
660 HWND hwndRichEdit, parent;
662 parent = new_static_wnd(NULL);
663 hwndRichEdit = new_richedit(parent);
664 /* Try and pass EM_AUTOURLDETECT some test wParam values */
665 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
666 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
667 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
668 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
669 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
670 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
671 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
672 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
673 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
674 /* for each url, check the text to see if CFE_LINK effect is present */
675 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
676 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
677 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
678 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
679 check_CFE_LINK_rcvd(hwndRichEdit, 0);
680 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
681 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
682 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
683 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
685 DestroyWindow(hwndRichEdit);
686 DestroyWindow(parent);
689 static void test_EM_SCROLL(void)
691 int i, j;
692 int r; /* return value */
693 int expr; /* expected return value */
694 HWND hwndRichEdit = new_richedit(NULL);
695 int y_before, y_after; /* units of lines of text */
697 /* test a richedit box containing a single line of text */
698 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
699 expr = 0x00010000;
700 for (i = 0; i < 4; i++) {
701 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
703 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
704 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
705 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
706 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
707 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
708 "(i == %d)\n", y_after, i);
712 * test a richedit box that will scroll. There are two general
713 * cases: the case without any long lines and the case with a long
714 * line.
716 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
717 if (i == 0)
718 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
719 else
720 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
721 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
722 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
723 "LONG LINE \nb\nc\nd\ne");
724 for (j = 0; j < 12; j++) /* reset scrol position to top */
725 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
727 /* get first visible line */
728 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
729 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
731 /* get new current first visible line */
732 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
734 ok(((r & 0xffffff00) == 0x00010000) &&
735 ((r & 0x000000ff) != 0x00000000),
736 "EM_SCROLL page down didn't scroll by a small positive number of "
737 "lines (r == 0x%08x)\n", r);
738 ok(y_after > y_before, "EM_SCROLL page down not functioning "
739 "(line %d scrolled to line %d\n", y_before, y_after);
741 y_before = y_after;
743 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
744 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
745 ok(((r & 0xffffff00) == 0x0001ff00),
746 "EM_SCROLL page up didn't scroll by a small negative number of lines "
747 "(r == 0x%08x)\n", r);
748 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
749 "%d scrolled to line %d\n", y_before, y_after);
751 y_before = y_after;
753 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
755 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
757 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
758 "(r == 0x%08x)\n", r);
759 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
760 "1 line (%d scrolled to %d)\n", y_before, y_after);
762 y_before = y_after;
764 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
766 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
768 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
769 "(r == 0x%08x)\n", r);
770 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
771 "line (%d scrolled to %d)\n", y_before, y_after);
773 y_before = y_after;
775 r = SendMessage(hwndRichEdit, EM_SCROLL,
776 SB_LINEUP, 0); /* lineup beyond top */
778 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
780 ok(r == 0x00010000,
781 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
782 ok(y_before == y_after,
783 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
785 y_before = y_after;
787 r = SendMessage(hwndRichEdit, EM_SCROLL,
788 SB_PAGEUP, 0);/*page up beyond top */
790 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
792 ok(r == 0x00010000,
793 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
794 ok(y_before == y_after,
795 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
797 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
798 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
799 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
800 r = SendMessage(hwndRichEdit, EM_SCROLL,
801 SB_PAGEDOWN, 0); /* page down beyond bot */
802 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
804 ok(r == 0x00010000,
805 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
806 ok(y_before == y_after,
807 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
808 y_before, y_after);
810 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
811 SendMessage(hwndRichEdit, EM_SCROLL,
812 SB_LINEDOWN, 0); /* line down beyond bot */
813 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
815 ok(r == 0x00010000,
816 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
817 ok(y_before == y_after,
818 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
819 y_before, y_after);
821 DestroyWindow(hwndRichEdit);
824 static void test_EM_SETUNDOLIMIT(void)
826 /* cases we test for:
827 * default behaviour - limiting at 100 undo's
828 * undo disabled - setting a limit of 0
829 * undo limited - undo limit set to some to some number, like 2
830 * bad input - sending a negative number should default to 100 undo's */
832 HWND hwndRichEdit = new_richedit(NULL);
833 CHARRANGE cr;
834 int i;
835 int result;
837 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
838 cr.cpMin = 0;
839 cr.cpMax = 1;
840 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
841 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
842 also, multiple pastes don't combine like WM_CHAR would */
843 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
845 /* first case - check the default */
846 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
847 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
848 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
849 for (i=0; i<100; i++) /* Undo 100 of them */
850 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
851 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
852 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
854 /* second case - cannot undo */
855 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
856 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
857 SendMessage(hwndRichEdit,
858 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
859 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
860 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
862 /* third case - set it to an arbitrary number */
863 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
864 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
865 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
866 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
867 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
868 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
869 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
870 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
871 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
872 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
873 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
874 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
875 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
876 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
878 /* fourth case - setting negative numbers should default to 100 undos */
879 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
880 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
881 ok (result == 100,
882 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
884 DestroyWindow(hwndRichEdit);
887 static void test_ES_PASSWORD(void)
889 /* This isn't hugely testable, so we're just going to run it through its paces */
891 HWND hwndRichEdit = new_richedit(NULL);
892 WCHAR result;
894 /* First, check the default of a regular control */
895 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
896 ok (result == 0,
897 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
899 /* Now, set it to something normal */
900 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
901 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
902 ok (result == 120,
903 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
905 /* Now, set it to something odd */
906 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
907 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
908 ok (result == 1234,
909 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
910 DestroyWindow(hwndRichEdit);
913 static void test_EM_SETTEXTEX(void)
915 HWND hwndRichEdit = new_richedit(NULL);
916 SETTEXTEX setText;
917 GETTEXTEX getText;
918 WCHAR TestItem1[] = {'T', 'e', 's', 't',
919 'S', 'o', 'm', 'e',
920 'T', 'e', 'x', 't', 0};
921 #define MAX_BUF_LEN 1024
922 WCHAR buf[MAX_BUF_LEN];
923 int result;
924 CHARRANGE cr;
926 setText.codepage = 1200; /* no constant for unicode */
927 getText.codepage = 1200; /* no constant for unicode */
928 getText.cb = MAX_BUF_LEN;
929 getText.flags = GT_DEFAULT;
931 setText.flags = 0;
932 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
933 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
934 ok(lstrcmpW(buf, TestItem1) == 0,
935 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
937 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
938 (WPARAM)&setText, (LPARAM) NULL);
939 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
941 ok (result == 1,
942 "EM_SETTEXTEX returned %d, instead of 1\n",result);
943 ok(lstrlenW(buf) == 0,
944 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
946 /* put some text back */
947 setText.flags = 0;
948 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
949 /* select some text */
950 cr.cpMax = 1;
951 cr.cpMin = 3;
952 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
953 /* replace current selection */
954 setText.flags = ST_SELECTION;
955 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
956 (WPARAM)&setText, (LPARAM) NULL);
957 ok(result == 0,
958 "EM_SETTEXTEX with NULL lParam to replace selection"
959 " with no text should return 0. Got %i\n",
960 result);
962 /* put some text back */
963 setText.flags = 0;
964 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
965 /* select some text */
966 cr.cpMax = 1;
967 cr.cpMin = 3;
968 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
969 /* replace current selection */
970 setText.flags = ST_SELECTION;
971 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
972 (WPARAM)&setText, (LPARAM) TestItem1);
973 /* get text */
974 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
975 ok(result == lstrlenW(TestItem1),
976 "EM_SETTEXTEX with NULL lParam to replace selection"
977 " with no text should return 0. Got %i\n",
978 result);
979 ok(lstrlenW(buf) == 22,
980 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
981 lstrlenW(buf) );
983 DestroyWindow(hwndRichEdit);
986 static void test_EM_LIMITTEXT(void)
988 int ret;
990 HWND hwndRichEdit = new_richedit(NULL);
992 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
993 * about setting the length to -1 for multiline edit controls doesn't happen.
996 /* Don't check default gettextlimit case. That's done in other tests */
998 /* Set textlimit to 100 */
999 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
1000 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1001 ok (ret == 100,
1002 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1004 /* Set textlimit to 0 */
1005 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1006 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1007 ok (ret == 65536,
1008 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1010 /* Set textlimit to -1 */
1011 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1012 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1013 ok (ret == -1,
1014 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1016 /* Set textlimit to -2 */
1017 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1018 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1019 ok (ret == -2,
1020 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1022 DestroyWindow (hwndRichEdit);
1026 static void test_EM_EXLIMITTEXT(void)
1028 int i, selBegin, selEnd, len1, len2;
1029 int result;
1030 char text[1024 + 1];
1031 char buffer[1024 + 1];
1032 int textlimit = 0; /* multiple of 100 */
1033 HWND hwndRichEdit = new_richedit(NULL);
1035 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1036 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1038 textlimit = 256000;
1039 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1040 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1041 /* set higher */
1042 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1044 textlimit = 1000;
1045 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1046 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1047 /* set lower */
1048 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1050 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1051 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1052 /* default for WParam = 0 */
1053 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1055 textlimit = sizeof(text)-1;
1056 memset(text, 'W', textlimit);
1057 text[sizeof(text)-1] = 0;
1058 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1059 /* maxed out text */
1060 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1062 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1063 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1064 len1 = selEnd - selBegin;
1066 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1067 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1068 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1069 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1070 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1071 len2 = selEnd - selBegin;
1073 ok(len1 != len2,
1074 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1075 len1,len2,i);
1077 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1078 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1079 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1080 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1081 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1082 len1 = selEnd - selBegin;
1084 ok(len1 != len2,
1085 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1086 len1,len2,i);
1088 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1089 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1090 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1091 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1092 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1093 len2 = selEnd - selBegin;
1095 ok(len1 == len2,
1096 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1097 len1,len2,i);
1099 /* set text up to the limit, select all the text, then add a char */
1100 textlimit = 5;
1101 memset(text, 'W', textlimit);
1102 text[textlimit] = 0;
1103 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1104 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1105 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1106 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1107 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1108 result = strcmp(buffer, "A");
1109 ok(0 == result, "got string = \"%s\"\n", buffer);
1111 DestroyWindow(hwndRichEdit);
1114 static void test_EM_GETLIMITTEXT(void)
1116 int i;
1117 HWND hwndRichEdit = new_richedit(NULL);
1119 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1120 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1122 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1123 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1124 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1126 DestroyWindow(hwndRichEdit);
1129 static void test_WM_SETFONT(void)
1131 /* There is no invalid input or error conditions for this function.
1132 * NULL wParam and lParam just fall back to their default values
1133 * It should be noted that even if you use a gibberish name for your fonts
1134 * here, it will still work because the name is stored. They will display as
1135 * System, but will report their name to be whatever they were created as */
1137 HWND hwndRichEdit = new_richedit(NULL);
1138 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1139 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1140 FF_DONTCARE, "Marlett");
1141 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1142 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1143 FF_DONTCARE, "MS Sans Serif");
1144 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1145 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1146 FF_DONTCARE, "Courier");
1147 LOGFONTA sentLogFont;
1148 CHARFORMAT2A returnedCF2A;
1150 returnedCF2A.cbSize = sizeof(returnedCF2A);
1152 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1153 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1154 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1156 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1157 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1158 "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1159 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1161 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1162 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1163 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1164 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1165 "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1166 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1168 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1169 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1170 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1171 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1172 "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1173 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1175 /* This last test is special since we send in NULL. We clear the variables
1176 * and just compare to "System" instead of the sent in font name. */
1177 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1178 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1179 returnedCF2A.cbSize = sizeof(returnedCF2A);
1181 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1182 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1183 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1184 ok (!strcmp("System",returnedCF2A.szFaceName),
1185 "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1187 DestroyWindow(hwndRichEdit);
1191 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1192 LPBYTE pbBuff,
1193 LONG cb,
1194 LONG *pcb)
1196 const char** str = (const char**)dwCookie;
1197 int size = strlen(*str);
1198 if(size > 3) /* let's make it peice-meal for fun */
1199 size = 3;
1200 *pcb = cb;
1201 if (*pcb > size) {
1202 *pcb = size;
1204 if (*pcb > 0) {
1205 memcpy(pbBuff, *str, *pcb);
1206 *str += *pcb;
1208 return 0;
1211 static void test_EM_GETMODIFY(void)
1213 HWND hwndRichEdit = new_richedit(NULL);
1214 LRESULT result;
1215 SETTEXTEX setText;
1216 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1217 'S', 'o', 'm', 'e',
1218 'T', 'e', 'x', 't', 0};
1219 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1220 'S', 'o', 'm', 'e',
1221 'O', 't', 'h', 'e', 'r',
1222 'T', 'e', 'x', 't', 0};
1223 const char* streamText = "hello world";
1224 CHARFORMAT2 cf2;
1225 PARAFORMAT2 pf2;
1226 EDITSTREAM es;
1228 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1229 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1230 FF_DONTCARE, "Courier");
1232 setText.codepage = 1200; /* no constant for unicode */
1233 setText.flags = ST_KEEPUNDO;
1236 /* modify flag shouldn't be set when richedit is first created */
1237 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1238 ok (result == 0,
1239 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1241 /* setting modify flag should actually set it */
1242 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1243 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1244 ok (result != 0,
1245 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1247 /* clearing modify flag should actually clear it */
1248 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1249 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1250 ok (result == 0,
1251 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1253 /* setting font doesn't change modify flag */
1254 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1255 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1256 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1257 ok (result == 0,
1258 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1260 /* setting text should set modify flag */
1261 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1262 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1263 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1264 ok (result != 0,
1265 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1267 /* undo previous text doesn't reset modify flag */
1268 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1269 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1270 ok (result != 0,
1271 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1273 /* set text with no flag to keep undo stack should not set modify flag */
1274 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1275 setText.flags = 0;
1276 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1277 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1278 ok (result == 0,
1279 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1281 /* WM_SETTEXT doesn't modify */
1282 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1283 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1284 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1285 todo_wine {
1286 ok (result == 0,
1287 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1290 /* clear the text */
1291 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1292 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1293 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1294 ok (result == 0,
1295 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1297 /* replace text */
1298 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1299 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1300 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1301 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1302 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1303 ok (result != 0,
1304 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1306 /* copy/paste text 1 */
1307 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1308 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1309 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1310 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1311 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1312 ok (result != 0,
1313 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1315 /* copy/paste text 2 */
1316 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1317 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1318 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1319 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1320 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1321 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1322 ok (result != 0,
1323 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1325 /* press char */
1326 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1327 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1328 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1329 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1330 ok (result != 0,
1331 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1333 /* press del */
1334 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1335 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1336 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
1337 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1338 ok (result != 0,
1339 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
1341 /* set char format */
1342 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1343 cf2.cbSize = sizeof(CHARFORMAT2);
1344 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1345 (LPARAM) &cf2);
1346 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1347 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1348 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1349 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1350 ok (result != 0,
1351 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1353 /* set para format */
1354 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1355 pf2.cbSize = sizeof(PARAFORMAT2);
1356 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1357 (LPARAM) &pf2);
1358 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1359 pf2.wAlignment = PFA_RIGHT;
1360 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1361 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1362 ok (result == 0,
1363 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1365 /* EM_STREAM */
1366 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1367 es.dwCookie = (DWORD_PTR)&streamText;
1368 es.dwError = 0;
1369 es.pfnCallback = test_EM_GETMODIFY_esCallback;
1370 SendMessage(hwndRichEdit, EM_STREAMIN,
1371 (WPARAM)(SF_TEXT), (LPARAM)&es);
1372 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1373 ok (result != 0,
1374 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1376 DestroyWindow(hwndRichEdit);
1379 struct exsetsel_s {
1380 long min;
1381 long max;
1382 long expected_retval;
1383 int expected_getsel_start;
1384 int expected_getsel_end;
1385 int _exsetsel_todo_wine;
1386 int _getsel_todo_wine;
1389 const struct exsetsel_s exsetsel_tests[] = {
1390 /* sanity tests */
1391 {5, 10, 10, 5, 10, 0, 0},
1392 {15, 17, 17, 15, 17, 0, 0},
1393 /* test cpMax > strlen() */
1394 {0, 100, 18, 0, 18, 0, 1},
1395 /* test cpMin == cpMax */
1396 {5, 5, 5, 5, 5, 0, 0},
1397 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1398 {-1, 0, 5, 5, 5, 0, 0},
1399 {-1, 17, 5, 5, 5, 0, 0},
1400 {-1, 18, 5, 5, 5, 0, 0},
1401 /* test cpMin < 0 && cpMax < 0 */
1402 {-1, -1, 17, 17, 17, 0, 0},
1403 {-4, -5, 17, 17, 17, 0, 0},
1404 /* test cMin >=0 && cpMax < 0 (bug 6814) */
1405 {0, -1, 18, 0, 18, 0, 1},
1406 {17, -5, 18, 17, 18, 0, 1},
1407 {18, -3, 17, 17, 17, 0, 0},
1408 /* test if cpMin > cpMax */
1409 {15, 19, 18, 15, 18, 0, 1},
1410 {19, 15, 18, 15, 18, 0, 1}
1413 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1414 CHARRANGE cr;
1415 long result;
1416 int start, end;
1418 cr.cpMin = setsel->min;
1419 cr.cpMax = setsel->max;
1420 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1422 if (setsel->_exsetsel_todo_wine) {
1423 todo_wine {
1424 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1426 } else {
1427 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1430 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1432 if (setsel->_getsel_todo_wine) {
1433 todo_wine {
1434 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);
1436 } else {
1437 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);
1441 static void test_EM_EXSETSEL(void)
1443 HWND hwndRichEdit = new_richedit(NULL);
1444 int i;
1445 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1447 /* sending some text to the window */
1448 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1449 /* 01234567890123456*/
1450 /* 10 */
1452 for (i = 0; i < num_tests; i++) {
1453 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1456 DestroyWindow(hwndRichEdit);
1459 static void test_WM_PASTE(void)
1461 int result;
1462 char buffer[1024] = {0};
1463 const char* text1 = "testing paste\r";
1464 const char* text2 = "testing paste\r\rtesting paste";
1465 const char* text3 = "testing paste\rpaste\rtesting paste";
1466 HWND hwndRichEdit = new_richedit(NULL);
1468 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1469 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1470 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1471 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1472 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1473 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1474 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1475 result = strcmp(text1, buffer);
1476 ok(result == 0,
1477 "test paste: strcmp = %i\n", result);
1479 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1480 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1481 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1482 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1483 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1484 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1485 SendMessage(hwndRichEdit, WM_CHAR, 25, 0); /* ctrl-y */
1486 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1487 result = strcmp(buffer,text3);
1488 ok(result == 0,
1489 "test paste: strcmp = %i\n", result);
1491 DestroyWindow(hwndRichEdit);
1494 static void test_EM_FORMATRANGE(void)
1496 int r;
1497 FORMATRANGE fr;
1498 HDC hdc;
1499 HWND hwndRichEdit = new_richedit(NULL);
1501 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
1503 hdc = GetDC(hwndRichEdit);
1504 ok(hdc != NULL, "Could not get HDC\n");
1506 fr.hdc = fr.hdcTarget = hdc;
1507 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
1508 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
1509 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
1510 fr.chrg.cpMin = 0;
1511 fr.chrg.cpMax = 20;
1513 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1514 todo_wine {
1515 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1518 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1519 todo_wine {
1520 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
1523 fr.chrg.cpMin = 0;
1524 fr.chrg.cpMax = 10;
1526 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
1527 todo_wine {
1528 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
1531 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
1532 todo_wine {
1533 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
1536 DestroyWindow(hwndRichEdit);
1539 static int nCallbackCount = 0;
1541 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1542 LONG cb, LONG* pcb)
1544 const char text[] = {'t','e','s','t'};
1546 if (sizeof(text) <= cb)
1548 if ((int)dwCookie != nCallbackCount)
1550 *pcb = 0;
1551 return 0;
1554 memcpy (pbBuff, text, sizeof(text));
1555 *pcb = sizeof(text);
1557 nCallbackCount++;
1559 return 0;
1561 else
1562 return 1; /* indicates callback failed */
1565 static void test_EM_StreamIn_Undo(void)
1567 /* The purpose of this test is to determine when a EM_StreamIn should be
1568 * undoable. This is important because WM_PASTE currently uses StreamIn and
1569 * pasting should always be undoable but streaming isn't always.
1571 * cases to test:
1572 * StreamIn plain text without SFF_SELECTION.
1573 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1574 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1575 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1576 * Feel free to add tests for other text modes or StreamIn things.
1580 HWND hwndRichEdit = new_richedit(NULL);
1581 LRESULT result;
1582 EDITSTREAM es;
1583 char buffer[1024] = {0};
1584 const char randomtext[] = "Some text";
1586 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1588 /* StreamIn, no SFF_SELECTION */
1589 es.dwCookie = nCallbackCount;
1590 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1591 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1592 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1593 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1594 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1595 result = strcmp (buffer,"test");
1596 ok (result == 0,
1597 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1599 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1600 ok (result == FALSE,
1601 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1603 /* StreamIn, SFF_SELECTION, but nothing selected */
1604 es.dwCookie = nCallbackCount;
1605 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1606 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1607 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1608 SendMessage(hwndRichEdit, EM_STREAMIN,
1609 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1610 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1611 result = strcmp (buffer,"testSome text");
1612 ok (result == 0,
1613 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1615 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1616 ok (result == TRUE,
1617 "EM_STREAMIN with SFF_SELECTION but no selection set "
1618 "should create an undo\n");
1620 /* StreamIn, SFF_SELECTION, with a selection */
1621 es.dwCookie = nCallbackCount;
1622 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1623 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1624 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1625 SendMessage(hwndRichEdit, EM_STREAMIN,
1626 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1627 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1628 result = strcmp (buffer,"Sometesttext");
1629 ok (result == 0,
1630 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1632 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1633 ok (result == TRUE,
1634 "EM_STREAMIN with SFF_SELECTION and selection set "
1635 "should create an undo\n");
1639 static BOOL is_em_settextex_supported(HWND hwnd)
1641 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
1642 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
1645 static void test_unicode_conversions(void)
1647 static const WCHAR tW[] = {'t',0};
1648 static const WCHAR teW[] = {'t','e',0};
1649 static const WCHAR textW[] = {'t','e','s','t',0};
1650 static const char textA[] = "test";
1651 char bufA[64];
1652 WCHAR bufW[64];
1653 HWND hwnd;
1654 int is_win9x, em_settextex_supported, ret;
1656 is_win9x = GetVersion() & 0x80000000;
1658 #define set_textA(hwnd, wm_set_text, txt) \
1659 do { \
1660 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1661 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1662 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1663 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1664 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1665 } while(0)
1666 #define expect_textA(hwnd, wm_get_text, txt) \
1667 do { \
1668 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1669 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1670 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1671 memset(bufA, 0xAA, sizeof(bufA)); \
1672 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1673 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1674 ret = lstrcmpA(bufA, txt); \
1675 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1676 } while(0)
1678 #define set_textW(hwnd, wm_set_text, txt) \
1679 do { \
1680 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1681 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1682 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1683 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1684 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1685 } while(0)
1686 #define expect_textW(hwnd, wm_get_text, txt) \
1687 do { \
1688 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1689 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1690 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1691 memset(bufW, 0xAA, sizeof(bufW)); \
1692 if (is_win9x) \
1694 assert(wm_get_text == EM_GETTEXTEX); \
1695 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1696 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1698 else \
1700 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1701 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1703 ret = lstrcmpW(bufW, txt); \
1704 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1705 } while(0)
1706 #define expect_empty(hwnd, wm_get_text) \
1707 do { \
1708 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1709 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1710 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1711 memset(bufA, 0xAA, sizeof(bufA)); \
1712 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1713 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1714 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1715 } while(0)
1717 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1718 0, 0, 200, 60, 0, 0, 0, 0);
1719 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1721 ret = IsWindowUnicode(hwnd);
1722 if (is_win9x)
1723 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1724 else
1725 ok(ret, "RichEdit20W should be unicode under NT\n");
1727 /* EM_SETTEXTEX is supported starting from version 3.0 */
1728 em_settextex_supported = is_em_settextex_supported(hwnd);
1729 trace("EM_SETTEXTEX is %ssupported on this platform\n",
1730 em_settextex_supported ? "" : "NOT ");
1732 expect_empty(hwnd, WM_GETTEXT);
1733 expect_empty(hwnd, EM_GETTEXTEX);
1735 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1736 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1737 expect_textA(hwnd, WM_GETTEXT, "t");
1738 expect_textA(hwnd, EM_GETTEXTEX, "t");
1739 expect_textW(hwnd, EM_GETTEXTEX, tW);
1741 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1742 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1743 expect_textA(hwnd, WM_GETTEXT, "te");
1744 expect_textA(hwnd, EM_GETTEXTEX, "te");
1745 expect_textW(hwnd, EM_GETTEXTEX, teW);
1747 set_textA(hwnd, WM_SETTEXT, NULL);
1748 expect_empty(hwnd, WM_GETTEXT);
1749 expect_empty(hwnd, EM_GETTEXTEX);
1751 if (is_win9x)
1752 set_textA(hwnd, WM_SETTEXT, textW);
1753 else
1754 set_textA(hwnd, WM_SETTEXT, textA);
1755 expect_textA(hwnd, WM_GETTEXT, textA);
1756 expect_textA(hwnd, EM_GETTEXTEX, textA);
1757 expect_textW(hwnd, EM_GETTEXTEX, textW);
1759 if (em_settextex_supported)
1761 set_textA(hwnd, EM_SETTEXTEX, textA);
1762 expect_textA(hwnd, WM_GETTEXT, textA);
1763 expect_textA(hwnd, EM_GETTEXTEX, textA);
1764 expect_textW(hwnd, EM_GETTEXTEX, textW);
1767 if (!is_win9x)
1769 set_textW(hwnd, WM_SETTEXT, textW);
1770 expect_textW(hwnd, WM_GETTEXT, textW);
1771 expect_textA(hwnd, WM_GETTEXT, textA);
1772 expect_textW(hwnd, EM_GETTEXTEX, textW);
1773 expect_textA(hwnd, EM_GETTEXTEX, textA);
1775 if (em_settextex_supported)
1777 set_textW(hwnd, EM_SETTEXTEX, textW);
1778 expect_textW(hwnd, WM_GETTEXT, textW);
1779 expect_textA(hwnd, WM_GETTEXT, textA);
1780 expect_textW(hwnd, EM_GETTEXTEX, textW);
1781 expect_textA(hwnd, EM_GETTEXTEX, textA);
1784 DestroyWindow(hwnd);
1786 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
1787 0, 0, 200, 60, 0, 0, 0, 0);
1788 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1790 ret = IsWindowUnicode(hwnd);
1791 ok(!ret, "RichEdit20A should NOT be unicode\n");
1793 set_textA(hwnd, WM_SETTEXT, textA);
1794 expect_textA(hwnd, WM_GETTEXT, textA);
1795 expect_textA(hwnd, EM_GETTEXTEX, textA);
1796 expect_textW(hwnd, EM_GETTEXTEX, textW);
1798 if (em_settextex_supported)
1800 set_textA(hwnd, EM_SETTEXTEX, textA);
1801 expect_textA(hwnd, WM_GETTEXT, textA);
1802 expect_textA(hwnd, EM_GETTEXTEX, textA);
1803 expect_textW(hwnd, EM_GETTEXTEX, textW);
1806 if (!is_win9x)
1808 set_textW(hwnd, WM_SETTEXT, textW);
1809 expect_textW(hwnd, WM_GETTEXT, textW);
1810 expect_textA(hwnd, WM_GETTEXT, textA);
1811 expect_textW(hwnd, EM_GETTEXTEX, textW);
1812 expect_textA(hwnd, EM_GETTEXTEX, textA);
1814 if (em_settextex_supported)
1816 set_textW(hwnd, EM_SETTEXTEX, textW);
1817 expect_textW(hwnd, WM_GETTEXT, textW);
1818 expect_textA(hwnd, WM_GETTEXT, textA);
1819 expect_textW(hwnd, EM_GETTEXTEX, textW);
1820 expect_textA(hwnd, EM_GETTEXTEX, textA);
1823 DestroyWindow(hwnd);
1826 START_TEST( editor )
1828 MSG msg;
1829 time_t end;
1831 /* Must explicitly LoadLibrary(). The test has no references to functions in
1832 * RICHED20.DLL, so the linker doesn't actually link to it. */
1833 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
1834 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1836 test_EM_FINDTEXT();
1837 test_EM_GETLINE();
1838 test_EM_SCROLLCARET();
1839 test_EM_SCROLL();
1840 test_EM_SETTEXTMODE();
1841 test_TM_PLAINTEXT();
1842 test_EM_SETOPTIONS();
1843 test_WM_GETTEXT();
1844 test_EM_AUTOURLDETECT();
1845 test_EM_SETUNDOLIMIT();
1846 test_ES_PASSWORD();
1847 test_EM_SETTEXTEX();
1848 test_EM_LIMITTEXT();
1849 test_EM_EXLIMITTEXT();
1850 test_EM_GETLIMITTEXT();
1851 test_WM_SETFONT();
1852 test_EM_GETMODIFY();
1853 test_EM_EXSETSEL();
1854 test_WM_PASTE();
1855 test_EM_StreamIn_Undo();
1856 test_EM_FORMATRANGE();
1857 test_unicode_conversions();
1859 /* Set the environment variable WINETEST_RICHED20 to keep windows
1860 * responsive and open for 30 seconds. This is useful for debugging.
1862 * The message pump uses PeekMessage() to empty the queue and then sleeps for
1863 * 50ms before retrying the queue. */
1864 end = time(NULL) + 30;
1865 if (getenv( "WINETEST_RICHED20" )) {
1866 while (time(NULL) < end) {
1867 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1868 TranslateMessage(&msg);
1869 DispatchMessage(&msg);
1870 } else {
1871 Sleep(50);
1876 OleFlushClipboard();
1877 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());