Changes in crossover-wine-src-6.1.0 except for configure
[wine/hacks.git] / dlls / riched20 / tests / editor.c
blobd1a1fac22675d9cb297fce78c8734eb27b0c4fc7
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 <wine/test.h>
34 static HMODULE hmoduleRichEdit;
36 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
37 HWND hwnd;
38 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
39 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
40 hmoduleRichEdit, NULL);
41 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
42 return hwnd;
45 static HWND new_richedit(HWND parent) {
46 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
49 static const char haystack[] = "WINEWine wineWine wine WineWine";
50 /* ^0 ^10 ^20 ^30 */
52 struct find_s {
53 int start;
54 int end;
55 const char *needle;
56 int flags;
57 int expected_loc;
58 int _todo_wine;
62 struct find_s find_tests[] = {
63 /* Find in empty text */
64 {0, -1, "foo", FR_DOWN, -1, 0},
65 {0, -1, "foo", 0, -1, 0},
66 {0, -1, "", FR_DOWN, -1, 0},
67 {20, 5, "foo", FR_DOWN, -1, 0},
68 {5, 20, "foo", FR_DOWN, -1, 0}
71 struct find_s find_tests2[] = {
72 /* No-result find */
73 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
74 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
76 /* Subsequent finds */
77 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
78 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
79 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
80 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
82 /* Find backwards */
83 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
84 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
85 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
87 /* Case-insensitive */
88 {1, 31, "wInE", FR_DOWN, 4, 0},
89 {1, 31, "Wine", FR_DOWN, 4, 0},
91 /* High-to-low ranges */
92 {20, 5, "Wine", FR_DOWN, -1, 0},
93 {2, 1, "Wine", FR_DOWN, -1, 0},
94 {30, 29, "Wine", FR_DOWN, -1, 0},
95 {20, 5, "Wine", 0, 13, 0},
97 /* Find nothing */
98 {5, 10, "", FR_DOWN, -1, 0},
99 {10, 5, "", FR_DOWN, -1, 0},
100 {0, -1, "", FR_DOWN, -1, 0},
101 {10, 5, "", 0, -1, 0},
103 /* Whole-word search */
104 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
105 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
106 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
107 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
108 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
109 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
110 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
112 /* Bad ranges */
113 {5, 200, "XXX", FR_DOWN, -1, 0},
114 {-20, 20, "Wine", FR_DOWN, -1, 0},
115 {-20, 20, "Wine", FR_DOWN, -1, 0},
116 {-15, -20, "Wine", FR_DOWN, -1, 0},
117 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
119 /* Check the case noted in bug 4479 where matches at end aren't recognized */
120 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
121 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
122 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
123 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
124 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
126 /* The backwards case of bug 4479; bounds look right
127 * Fails because backward find is wrong */
128 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
129 {0, 20, "WINE", FR_MATCHCASE, -1, 0}
132 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
133 int findloc;
134 FINDTEXT ft;
135 memset(&ft, 0, sizeof(ft));
136 ft.chrg.cpMin = f->start;
137 ft.chrg.cpMax = f->end;
138 ft.lpstrText = f->needle;
139 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
140 ok(findloc == f->expected_loc,
141 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
142 name, id, f->needle, f->start, f->end, f->flags, findloc);
145 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
146 int id) {
147 int findloc;
148 FINDTEXTEX ft;
149 memset(&ft, 0, sizeof(ft));
150 ft.chrg.cpMin = f->start;
151 ft.chrg.cpMax = f->end;
152 ft.lpstrText = f->needle;
153 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
154 ok(findloc == f->expected_loc,
155 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
156 name, id, f->needle, f->start, f->end, f->flags, findloc);
157 ok(ft.chrgText.cpMin == f->expected_loc,
158 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
159 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
160 ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
161 : f->expected_loc + strlen(f->needle)),
162 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n",
163 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
166 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
167 int num_tests)
169 int i;
171 for (i = 0; i < num_tests; i++) {
172 if (find[i]._todo_wine) {
173 todo_wine {
174 check_EM_FINDTEXT(hwnd, name, &find[i], i);
175 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
177 } else {
178 check_EM_FINDTEXT(hwnd, name, &find[i], i);
179 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
184 static void test_EM_FINDTEXT(void)
186 HWND hwndRichEdit = new_richedit(NULL);
188 /* Empty rich edit control */
189 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
190 sizeof(find_tests)/sizeof(struct find_s));
192 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
194 /* Haystack text */
195 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
196 sizeof(find_tests2)/sizeof(struct find_s));
198 DestroyWindow(hwndRichEdit);
201 static const struct getline_s {
202 int line;
203 size_t buffer_len;
204 const char *text;
205 } gl[] = {
206 {0, 10, "foo bar\r"},
207 {1, 10, "\r"},
208 {2, 10, "bar\r"},
209 {3, 10, "\r"},
211 /* Buffer smaller than line length */
212 {0, 2, "foo bar\r"},
213 {0, 1, "foo bar\r"},
214 {0, 0, "foo bar\r"}
217 static void test_EM_GETLINE(void)
219 int i;
220 HWND hwndRichEdit = new_richedit(NULL);
221 static const int nBuf = 1024;
222 char dest[1024], origdest[1024];
223 const char text[] = "foo bar\n"
224 "\n"
225 "bar\n";
227 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
229 memset(origdest, 0xBB, nBuf);
230 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
232 int nCopied;
233 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
234 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
235 memset(dest, 0xBB, nBuf);
236 *(WORD *) dest = gl[i].buffer_len;
238 /* EM_GETLINE appends a "\r\0" to the end of the line
239 * nCopied counts up to and including the '\r' */
240 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
241 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
242 expected_nCopied);
243 /* two special cases since a parameter is passed via dest */
244 if (gl[i].buffer_len == 0)
245 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
246 "buffer_len=0\n");
247 else if (gl[i].buffer_len == 1)
248 ok(dest[0] == gl[i].text[0] && !dest[1] &&
249 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
250 else
252 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
253 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
254 ok(!strncmp(dest + expected_bytes_written, origdest
255 + expected_bytes_written, nBuf - expected_bytes_written),
256 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
260 DestroyWindow(hwndRichEdit);
263 static int get_scroll_pos_y(HWND hwnd)
265 POINT p = {-1, -1};
266 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
267 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
268 return p.y;
271 static void move_cursor(HWND hwnd, long charindex)
273 CHARRANGE cr;
274 cr.cpMax = charindex;
275 cr.cpMin = charindex;
276 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
279 static void line_scroll(HWND hwnd, int amount)
281 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
284 static void test_EM_SCROLLCARET(void)
286 int prevY, curY;
287 HWND hwndRichEdit = new_richedit(NULL);
288 const char text[] = "aa\n"
289 "this is a long line of text that should be longer than the "
290 "control's width\n"
291 "cc\n"
292 "dd\n"
293 "ee\n"
294 "ff\n"
295 "gg\n"
296 "hh\n";
298 /* Can't verify this */
299 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
301 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
303 /* Caret above visible window */
304 line_scroll(hwndRichEdit, 3);
305 prevY = get_scroll_pos_y(hwndRichEdit);
306 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
307 curY = get_scroll_pos_y(hwndRichEdit);
308 ok(prevY != curY, "%d == %d\n", prevY, curY);
310 /* Caret below visible window */
311 move_cursor(hwndRichEdit, sizeof(text) - 1);
312 line_scroll(hwndRichEdit, -3);
313 prevY = get_scroll_pos_y(hwndRichEdit);
314 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
315 curY = get_scroll_pos_y(hwndRichEdit);
316 ok(prevY != curY, "%d == %d\n", prevY, curY);
318 /* Caret in visible window */
319 move_cursor(hwndRichEdit, sizeof(text) - 2);
320 prevY = get_scroll_pos_y(hwndRichEdit);
321 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
322 curY = get_scroll_pos_y(hwndRichEdit);
323 ok(prevY == curY, "%d != %d\n", prevY, curY);
325 /* Caret still in visible window */
326 line_scroll(hwndRichEdit, -1);
327 prevY = get_scroll_pos_y(hwndRichEdit);
328 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
329 curY = get_scroll_pos_y(hwndRichEdit);
330 ok(prevY == curY, "%d != %d\n", prevY, curY);
332 DestroyWindow(hwndRichEdit);
335 static void test_EM_SETTEXTMODE(void)
337 HWND hwndRichEdit = new_richedit(NULL);
338 CHARFORMAT2 cf2, cf2test;
339 CHARRANGE cr;
340 int rc = 0;
342 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
343 /*Insert text into the control*/
345 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
347 /*Attempt to change the control to plain text mode*/
348 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
349 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
351 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
352 If rich text is pasted, it should have the same formatting as the rest
353 of the text in the control*/
355 /*Italicize the text
356 *NOTE: If the default text was already italicized, the test will simply
357 reverse; in other words, it will copy a regular "wine" into a plain
358 text window that uses an italicized format*/
359 cf2.cbSize = sizeof(CHARFORMAT2);
360 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
361 (LPARAM) &cf2);
363 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
364 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
366 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
367 however, SCF_ALL has been implemented*/
368 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
369 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
371 /*Select the string "wine"*/
372 cr.cpMin = 0;
373 cr.cpMax = 4;
374 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
376 /*Copy the italicized "wine" to the clipboard*/
377 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
379 /*Reset the formatting to default*/
380 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
381 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
383 /*Clear the text in the control*/
384 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
386 /*Switch to Plain Text Mode*/
387 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
388 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
390 /*Input "wine" again in normal format*/
391 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
393 /*Paste the italicized "wine" into the control*/
394 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
396 /*Select a character from the first "wine" string*/
397 cr.cpMin = 2;
398 cr.cpMax = 3;
399 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
401 /*Retrieve its formatting*/
402 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
403 (LPARAM) &cf2);
405 /*Select a character from the second "wine" string*/
406 cr.cpMin = 5;
407 cr.cpMax = 6;
408 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
410 /*Retrieve its formatting*/
411 cf2test.cbSize = sizeof(CHARFORMAT2);
412 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
413 (LPARAM) &cf2test);
415 /*Compare the two formattings*/
416 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
417 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
418 cf2.dwEffects, cf2test.dwEffects);
419 /*Test TM_RICHTEXT by: switching back to Rich Text mode
420 printing "wine" in the current format(normal)
421 pasting "wine" from the clipboard(italicized)
422 comparing the two formats(should differ)*/
424 /*Attempt to switch with text in control*/
425 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
426 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
428 /*Clear control*/
429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
431 /*Switch into Rich Text mode*/
432 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
433 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
435 /*Print "wine" in normal formatting into the control*/
436 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
438 /*Paste italicized "wine" into the control*/
439 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
441 /*Select text from the first "wine" string*/
442 cr.cpMin = 1;
443 cr.cpMax = 3;
444 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
446 /*Retrieve its formatting*/
447 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
448 (LPARAM) &cf2);
450 /*Select text from the second "wine" string*/
451 cr.cpMin = 6;
452 cr.cpMax = 7;
453 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
455 /*Retrieve its formatting*/
456 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
457 (LPARAM) &cf2test);
459 /*Test that the two formattings are not the same*/
460 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
461 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
462 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
464 DestroyWindow(hwndRichEdit);
467 static void test_TM_PLAINTEXT(void)
469 /*Tests plain text properties*/
471 HWND hwndRichEdit = new_richedit(NULL);
472 CHARFORMAT2 cf2, cf2test;
473 CHARRANGE cr;
475 /*Switch to plain text mode*/
477 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
478 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
480 /*Fill control with text*/
482 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
484 /*Select some text and bold it*/
486 cr.cpMin = 10;
487 cr.cpMax = 20;
488 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
489 cf2.cbSize = sizeof(CHARFORMAT2);
490 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
491 (LPARAM) &cf2);
493 cf2.dwMask = CFM_BOLD | cf2.dwMask;
494 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
496 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
498 /*Get the formatting of those characters*/
500 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
502 /*Get the formatting of some other characters*/
503 cf2test.cbSize = sizeof(CHARFORMAT2);
504 cr.cpMin = 21;
505 cr.cpMax = 30;
506 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
507 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
509 /*Test that they are the same as plain text allows only one formatting*/
511 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
512 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
513 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
515 /*Fill the control with a "wine" string, which when inserted will be bold*/
517 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
519 /*Copy the bolded "wine" string*/
521 cr.cpMin = 0;
522 cr.cpMax = 4;
523 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
524 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
526 /*Swap back to rich text*/
528 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
529 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
531 /*Set the default formatting to bold italics*/
533 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
534 cf2.dwMask |= CFM_ITALIC;
535 cf2.dwEffects ^= CFE_ITALIC;
536 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
538 /*Set the text in the control to "wine", which will be bold and italicized*/
540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
542 /*Paste the plain text "wine" string, which should take the insert
543 formatting, which at the moment is bold italics*/
545 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
547 /*Select the first "wine" string and retrieve its formatting*/
549 cr.cpMin = 1;
550 cr.cpMax = 3;
551 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
552 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
554 /*Select the second "wine" string and retrieve its formatting*/
556 cr.cpMin = 5;
557 cr.cpMax = 7;
558 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
559 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
561 /*Compare the two formattings. They should be the same.*/
563 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
564 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
565 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
566 DestroyWindow(hwndRichEdit);
569 static void test_WM_GETTEXT(void)
571 HWND hwndRichEdit = new_richedit(NULL);
572 static const char text[] = "Hello. My name is RichEdit!";
573 char buffer[1024] = {0};
574 int result;
576 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
577 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
578 result = strcmp(buffer,text);
579 ok(result == 0,
580 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
581 DestroyWindow(hwndRichEdit);
584 /* FIXME: need to test unimplemented options and robustly test wparam */
585 static void test_EM_SETOPTIONS(void)
587 HWND hwndRichEdit = new_richedit(NULL);
588 static const char text[] = "Hello. My name is RichEdit!";
589 char buffer[1024] = {0};
591 /* NEGATIVE TESTING - NO OPTIONS SET */
592 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
593 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
595 /* testing no readonly by sending 'a' to the control*/
596 SetFocus(hwndRichEdit);
597 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
598 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
599 ok(buffer[0]=='a',
600 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
601 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
603 /* READONLY - sending 'a' to the control */
604 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
605 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
606 SetFocus(hwndRichEdit);
607 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
608 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
609 ok(buffer[0]==text[0],
610 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
612 DestroyWindow(hwndRichEdit);
615 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
617 CHARFORMAT2W text_format;
618 int link_present = 0;
619 text_format.cbSize = sizeof(text_format);
620 SendMessage(hwnd, EM_SETSEL, 0, 0);
621 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
622 link_present = text_format.dwEffects & CFE_LINK;
623 if (is_url)
624 { /* control text is url; should get CFE_LINK */
625 ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
627 else
629 ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
633 static HWND new_static_wnd(HWND parent) {
634 return new_window("Static", 0, parent);
637 static void test_EM_AUTOURLDETECT(void)
639 struct urls_s {
640 const char *text;
641 int is_url;
642 } urls[12] = {
643 {"winehq.org", 0},
644 {"http://www.winehq.org", 1},
645 {"http//winehq.org", 0},
646 {"ww.winehq.org", 0},
647 {"www.winehq.org", 1},
648 {"ftp://192.168.1.1", 1},
649 {"ftp//192.168.1.1", 0},
650 {"mailto:your@email.com", 1},
651 {"prospero:prosperoserver", 1},
652 {"telnet:test", 1},
653 {"news:newserver", 1},
654 {"wais:waisserver", 1}
657 int i;
658 int urlRet=-1;
659 HWND hwndRichEdit, parent;
661 parent = new_static_wnd(NULL);
662 hwndRichEdit = new_richedit(parent);
663 /* Try and pass EM_AUTOURLDETECT some test wParam values */
664 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
665 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
666 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
667 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
668 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
669 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
670 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
671 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
672 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
673 /* for each url, check the text to see if CFE_LINK effect is present */
674 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
675 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
676 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
677 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
678 check_CFE_LINK_rcvd(hwndRichEdit, 0);
679 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
680 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
681 SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
682 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
684 DestroyWindow(hwndRichEdit);
685 DestroyWindow(parent);
688 static void test_EM_SCROLL(void)
690 int i, j;
691 int r; /* return value */
692 int expr; /* expected return value */
693 HWND hwndRichEdit = new_richedit(NULL);
694 int y_before, y_after; /* units of lines of text */
696 /* test a richedit box containing a single line of text */
697 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
698 expr = 0x00010000;
699 for (i = 0; i < 4; i++) {
700 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
702 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
703 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
704 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
705 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
706 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
707 "(i == %d)\n", y_after, i);
711 * test a richedit box that will scroll. There are two general
712 * cases: the case without any long lines and the case with a long
713 * line.
715 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
716 if (i == 0)
717 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
718 else
719 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
720 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
721 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
722 "LONG LINE \nb\nc\nd\ne");
723 for (j = 0; j < 12; j++) /* reset scrol position to top */
724 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
726 /* get first visible line */
727 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
728 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
730 /* get new current first visible line */
731 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
733 ok(((r & 0xffffff00) == 0x00010000) &&
734 ((r & 0x000000ff) != 0x00000000),
735 "EM_SCROLL page down didn't scroll by a small positive number of "
736 "lines (r == 0x%08x)\n", r);
737 ok(y_after > y_before, "EM_SCROLL page down not functioning "
738 "(line %d scrolled to line %d\n", y_before, y_after);
740 y_before = y_after;
742 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
743 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
744 ok(((r & 0xffffff00) == 0x0001ff00),
745 "EM_SCROLL page up didn't scroll by a small negative number of lines "
746 "(r == 0x%08x)\n", r);
747 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
748 "%d scrolled to line %d\n", y_before, y_after);
750 y_before = y_after;
752 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
754 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
756 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
757 "(r == 0x%08x)\n", r);
758 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
759 "1 line (%d scrolled to %d)\n", y_before, y_after);
761 y_before = y_after;
763 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
765 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
767 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
768 "(r == 0x%08x)\n", r);
769 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
770 "line (%d scrolled to %d)\n", y_before, y_after);
772 y_before = y_after;
774 r = SendMessage(hwndRichEdit, EM_SCROLL,
775 SB_LINEUP, 0); /* lineup beyond top */
777 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
779 ok(r == 0x00010000,
780 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
781 ok(y_before == y_after,
782 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
784 y_before = y_after;
786 r = SendMessage(hwndRichEdit, EM_SCROLL,
787 SB_PAGEUP, 0);/*page up beyond top */
789 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
791 ok(r == 0x00010000,
792 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
793 ok(y_before == y_after,
794 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
796 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
797 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
798 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
799 r = SendMessage(hwndRichEdit, EM_SCROLL,
800 SB_PAGEDOWN, 0); /* page down beyond bot */
801 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
803 ok(r == 0x00010000,
804 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
805 ok(y_before == y_after,
806 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
807 y_before, y_after);
809 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
810 SendMessage(hwndRichEdit, EM_SCROLL,
811 SB_LINEDOWN, 0); /* line down beyond bot */
812 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
814 ok(r == 0x00010000,
815 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
816 ok(y_before == y_after,
817 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
818 y_before, y_after);
820 DestroyWindow(hwndRichEdit);
823 static void test_EM_SETUNDOLIMIT(void)
825 /* cases we test for:
826 * default behaviour - limiting at 100 undo's
827 * undo disabled - setting a limit of 0
828 * undo limited - undo limit set to some to some number, like 2
829 * bad input - sending a negative number should default to 100 undo's */
831 HWND hwndRichEdit = new_richedit(NULL);
832 CHARRANGE cr;
833 int i;
834 int result;
836 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
837 cr.cpMin = 0;
838 cr.cpMax = 1;
839 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
840 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
841 also, multiple pastes don't combine like WM_CHAR would */
842 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
844 /* first case - check the default */
845 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
846 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
847 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
848 for (i=0; i<100; i++) /* Undo 100 of them */
849 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
850 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
851 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
853 /* second case - cannot undo */
854 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
855 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
856 SendMessage(hwndRichEdit,
857 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
858 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
859 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
861 /* third case - set it to an arbitrary number */
862 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
863 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
864 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
865 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
866 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
867 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
868 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
869 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
870 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
871 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
872 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
873 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
874 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
875 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
877 /* fourth case - setting negative numbers should default to 100 undos */
878 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
879 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
880 ok (result == 100,
881 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
883 DestroyWindow(hwndRichEdit);
886 static void test_ES_PASSWORD(void)
888 /* This isn't hugely testable, so we're just going to run it through it's paces. */
890 HWND hwndRichEdit = new_richedit(NULL);
891 WCHAR result;
893 /* First, check the default of a regular control */
894 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
895 ok (result == 0,
896 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
898 /* Now, set it to something normal */
899 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
900 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
901 ok (result == 120,
902 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
904 /* Now, set it to something odd */
905 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
906 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
907 ok (result == 1234,
908 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
909 DestroyWindow(hwndRichEdit);
912 static void test_EM_SETTEXTEX(void)
914 HWND hwndRichEdit = new_richedit(NULL);
915 SETTEXTEX setText;
916 GETTEXTEX getText;
917 WCHAR TestItem1[] = {'T', 'e', 's', 't',
918 'S', 'o', 'm', 'e',
919 'T', 'e', 'x', 't', 0};
920 #define MAX_BUF_LEN 1024
921 WCHAR buf[MAX_BUF_LEN];
922 int result;
923 CHARRANGE cr;
925 setText.codepage = 1200; /* no constant for unicode */
926 getText.codepage = 1200; /* no constant for unicode */
927 getText.cb = MAX_BUF_LEN;
928 getText.flags = GT_DEFAULT;
930 setText.flags = 0;
931 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
932 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
933 ok(lstrcmpW(buf, TestItem1) == 0,
934 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
936 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
937 (WPARAM)&setText, (LPARAM) NULL);
938 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
940 ok (result == 1,
941 "EM_SETTEXTEX returned %d, instead of 1\n",result);
942 ok(lstrlenW(buf) == 0,
943 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
945 /* put some text back */
946 setText.flags = 0;
947 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
948 /* select some text */
949 cr.cpMax = 1;
950 cr.cpMin = 3;
951 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
952 /* replace current selection */
953 setText.flags = ST_SELECTION;
954 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
955 (WPARAM)&setText, (LPARAM) NULL);
956 ok(result == 0,
957 "EM_SETTEXTEX with NULL lParam to replace selection"
958 " with no text should return 0. Got %i\n",
959 result);
961 /* put some text back */
962 setText.flags = 0;
963 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
964 /* select some text */
965 cr.cpMax = 1;
966 cr.cpMin = 3;
967 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
968 /* replace current selection */
969 setText.flags = ST_SELECTION;
970 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
971 (WPARAM)&setText, (LPARAM) TestItem1);
972 /* get text */
973 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
974 ok(result == lstrlenW(TestItem1),
975 "EM_SETTEXTEX with NULL lParam to replace selection"
976 " with no text should return 0. Got %i\n",
977 result);
978 ok(lstrlenW(buf) == 22,
979 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
980 lstrlenW(buf) );
982 DestroyWindow(hwndRichEdit);
985 static void test_EM_LIMITTEXT(void)
987 int ret;
989 HWND hwndRichEdit = new_richedit(NULL);
991 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
992 * about setting the length to -1 for multiline edit controls doesn't happen.
995 /* Don't check default gettextlimit case. That's done in other tests */
997 /* Set textlimit to 100 */
998 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
999 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1000 ok (ret == 100,
1001 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
1003 /* Set textlimit to 0 */
1004 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
1005 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1006 ok (ret == 65536,
1007 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
1009 /* Set textlimit to -1 */
1010 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
1011 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1012 ok (ret == -1,
1013 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
1015 /* Set textlimit to -2 */
1016 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
1017 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1018 ok (ret == -2,
1019 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
1021 DestroyWindow (hwndRichEdit);
1025 static void test_EM_EXLIMITTEXT(void)
1027 int i, selBegin, selEnd, len1, len2;
1028 int result;
1029 char text[1024 + 1];
1030 char buffer[1024 + 1];
1031 int textlimit = 0; /* multiple of 100 */
1032 HWND hwndRichEdit = new_richedit(NULL);
1034 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1035 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
1037 textlimit = 256000;
1038 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1039 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1040 /* set higher */
1041 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1043 textlimit = 1000;
1044 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1045 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1046 /* set lower */
1047 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
1049 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1050 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1051 /* default for WParam = 0 */
1052 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1054 textlimit = sizeof(text)-1;
1055 memset(text, 'W', textlimit);
1056 text[sizeof(text)-1] = 0;
1057 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1058 /* maxed out text */
1059 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1061 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
1062 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1063 len1 = selEnd - selBegin;
1065 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1066 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1067 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1068 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1069 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1070 len2 = selEnd - selBegin;
1072 ok(len1 != len2,
1073 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1074 len1,len2,i);
1076 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1077 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1078 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1079 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1080 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1081 len1 = selEnd - selBegin;
1083 ok(len1 != len2,
1084 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1085 len1,len2,i);
1087 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1088 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1089 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
1090 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1091 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1092 len2 = selEnd - selBegin;
1094 ok(len1 == len2,
1095 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1096 len1,len2,i);
1098 /* set text up to the limit, select all the text, then add a char */
1099 textlimit = 5;
1100 memset(text, 'W', textlimit);
1101 text[textlimit] = 0;
1102 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1103 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1104 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1105 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1106 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1107 result = strcmp(buffer, "A");
1108 ok(0 == result, "got string = \"%s\"\n", buffer);
1110 DestroyWindow(hwndRichEdit);
1113 static void test_EM_GETLIMITTEXT(void)
1115 int i;
1116 HWND hwndRichEdit = new_richedit(NULL);
1118 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1119 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1121 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1122 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1123 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1125 DestroyWindow(hwndRichEdit);
1128 static void test_WM_SETFONT(void)
1130 /* There is no invalid input or error conditions for this function.
1131 * NULL wParam and lParam just fall back to their default values
1132 * It should be noted that even if you use a gibberish name for your fonts
1133 * here, it will still work because the name is stored. They will display as
1134 * System, but will report their name to be whatever they were created as */
1136 HWND hwndRichEdit = new_richedit(NULL);
1137 HFONT testFont1 = 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, "Marlett");
1140 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1141 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1142 FF_DONTCARE, "MS Sans Serif");
1143 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1144 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1145 FF_DONTCARE, "Courier");
1146 LOGFONTA sentLogFont;
1147 CHARFORMAT2A returnedCF2A;
1149 returnedCF2A.cbSize = sizeof(returnedCF2A);
1151 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1152 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1153 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1155 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1156 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1157 "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1158 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1160 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1161 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1162 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1163 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1164 "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1165 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1167 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1168 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1169 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1170 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1171 "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1172 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1174 /* This last test is special since we send in NULL. We clear the variables
1175 * and just compare to "System" instead of the sent in font name. */
1176 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1177 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1178 returnedCF2A.cbSize = sizeof(returnedCF2A);
1180 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1181 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
1182 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1183 ok (!strcmp("System",returnedCF2A.szFaceName),
1184 "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1186 DestroyWindow(hwndRichEdit);
1190 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1191 LPBYTE pbBuff,
1192 LONG cb,
1193 LONG *pcb)
1195 const char** str = (const char**)dwCookie;
1196 int size = strlen(*str);
1197 if(size > 3) /* let's make it peice-meal for fun */
1198 size = 3;
1199 *pcb = cb;
1200 if (*pcb > size) {
1201 *pcb = size;
1203 if (*pcb > 0) {
1204 memcpy(pbBuff, *str, *pcb);
1205 *str += *pcb;
1207 return 0;
1210 static void test_EM_GETMODIFY(void)
1212 HWND hwndRichEdit = new_richedit(NULL);
1213 LRESULT result;
1214 SETTEXTEX setText;
1215 WCHAR TestItem1[] = {'T', 'e', 's', 't',
1216 'S', 'o', 'm', 'e',
1217 'T', 'e', 'x', 't', 0};
1218 WCHAR TestItem2[] = {'T', 'e', 's', 't',
1219 'S', 'o', 'm', 'e',
1220 'O', 't', 'h', 'e', 'r',
1221 'T', 'e', 'x', 't', 0};
1222 const char* streamText = "hello world";
1223 CHARFORMAT2 cf2;
1224 PARAFORMAT2 pf2;
1225 EDITSTREAM es;
1227 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
1228 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
1229 FF_DONTCARE, "Courier");
1231 setText.codepage = 1200; /* no constant for unicode */
1232 setText.flags = ST_KEEPUNDO;
1235 /* modify flag shouldn't be set when richedit is first created */
1236 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1237 ok (result == 0,
1238 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1240 /* setting modify flag should actually set it */
1241 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1242 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1243 ok (result != 0,
1244 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1246 /* clearing modify flag should actually clear it */
1247 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1248 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1249 ok (result == 0,
1250 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1252 /* setting font doesn't change modify flag */
1253 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1254 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1255 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1256 ok (result == 0,
1257 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1259 /* setting text should set modify flag */
1260 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1261 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1262 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1263 ok (result != 0,
1264 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1266 /* undo previous text doesn't reset modify flag */
1267 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1268 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1269 ok (result != 0,
1270 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1272 /* set text with no flag to keep undo stack should not set modify flag */
1273 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1274 setText.flags = 0;
1275 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1276 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1277 ok (result == 0,
1278 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1280 /* WM_SETTEXT doesn't modify */
1281 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1282 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1283 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1284 todo_wine {
1285 ok (result == 0,
1286 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1289 /* clear the text */
1290 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1291 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1292 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1293 ok (result == 0,
1294 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1296 /* replace text */
1297 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1298 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1299 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1300 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1301 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1302 ok (result != 0,
1303 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1305 /* copy/paste text 1 */
1306 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1307 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1308 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1309 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1310 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1311 ok (result != 0,
1312 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1314 /* copy/paste text 2 */
1315 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1316 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1317 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1318 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1319 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1320 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1321 ok (result != 0,
1322 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1324 /* press char */
1325 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1326 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1327 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1328 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1329 ok (result != 0,
1330 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1332 /* set char format */
1333 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1334 cf2.cbSize = sizeof(CHARFORMAT2);
1335 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1336 (LPARAM) &cf2);
1337 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1338 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1339 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1340 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1341 ok (result != 0,
1342 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1344 /* set para format */
1345 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1346 pf2.cbSize = sizeof(PARAFORMAT2);
1347 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1348 (LPARAM) &pf2);
1349 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1350 pf2.wAlignment = PFA_RIGHT;
1351 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1352 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1353 ok (result == 0,
1354 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1356 /* EM_STREAM */
1357 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1358 es.dwCookie = (DWORD_PTR)&streamText;
1359 es.dwError = 0;
1360 es.pfnCallback = test_EM_GETMODIFY_esCallback;
1361 SendMessage(hwndRichEdit, EM_STREAMIN,
1362 (WPARAM)(SF_TEXT), (LPARAM)&es);
1363 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1364 ok (result != 0,
1365 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1367 DestroyWindow(hwndRichEdit);
1370 struct exsetsel_s {
1371 long min;
1372 long max;
1373 long expected_retval;
1374 int expected_getsel_start;
1375 int expected_getsel_end;
1376 int _exsetsel_todo_wine;
1377 int _getsel_todo_wine;
1380 const struct exsetsel_s exsetsel_tests[] = {
1381 /* sanity tests */
1382 {5, 10, 10, 5, 10, 0, 0},
1383 {15, 17, 17, 15, 17, 0, 0},
1384 /* test cpMax > strlen() */
1385 {0, 100, 18, 0, 18, 0, 1},
1386 /* test cpMin == cpMax */
1387 {5, 5, 5, 5, 5, 0, 0},
1388 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1389 {-1, 0, 5, 5, 5, 0, 0},
1390 {-1, 17, 5, 5, 5, 0, 0},
1391 {-1, 18, 5, 5, 5, 0, 0},
1392 /* test cpMin < 0 && cpMax < 0 */
1393 {-1, -1, 17, 17, 17, 0, 0},
1394 {-4, -5, 17, 17, 17, 0, 0},
1395 /* test cMin >=0 && cpMax < 0 (bug 6814) */
1396 {0, -1, 18, 0, 18, 0, 1},
1397 {17, -5, 18, 17, 18, 0, 1},
1398 {18, -3, 17, 17, 17, 0, 0},
1399 /* test if cpMin > cpMax */
1400 {15, 19, 18, 15, 18, 0, 1},
1401 {19, 15, 18, 15, 18, 0, 1}
1404 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1405 CHARRANGE cr;
1406 long result;
1407 int start, end;
1409 cr.cpMin = setsel->min;
1410 cr.cpMax = setsel->max;
1411 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
1413 if (setsel->_exsetsel_todo_wine) {
1414 todo_wine {
1415 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1417 } else {
1418 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1421 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
1423 if (setsel->_getsel_todo_wine) {
1424 todo_wine {
1425 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);
1427 } else {
1428 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);
1432 static void test_EM_EXSETSEL(void)
1434 HWND hwndRichEdit = new_richedit(NULL);
1435 int i;
1436 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
1438 /* sending some text to the window */
1439 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
1440 /* 01234567890123456*/
1441 /* 10 */
1443 for (i = 0; i < num_tests; i++) {
1444 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1447 DestroyWindow(hwndRichEdit);
1450 static void test_WM_PASTE(void)
1452 int result;
1453 char buffer[1024] = {0};
1454 const char* text1 = "testing paste\r";
1455 const char* text2 = "testing paste\r\rtesting paste";
1456 const char* text3 = "testing paste\rpaste\rtesting paste";
1457 HWND hwndRichEdit = new_richedit(NULL);
1459 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
1460 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
1461 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1462 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1463 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1464 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1465 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1466 result = strcmp(text1, buffer);
1467 ok(result == 0,
1468 "test paste: strcmp = %i\n", result);
1470 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1471 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
1472 SendMessage(hwndRichEdit, WM_CHAR, 3, 0); /* ctrl-c */
1473 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
1474 SendMessage(hwndRichEdit, WM_CHAR, 22, 0); /* ctrl-v */
1475 SendMessage(hwndRichEdit, WM_CHAR, 26, 0); /* ctrl-z */
1476 SendMessage(hwndRichEdit, WM_CHAR, 25, 0); /* ctrl-y */
1477 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1478 result = strcmp(buffer,text3);
1479 ok(result == 0,
1480 "test paste: strcmp = %i\n", result);
1482 DestroyWindow(hwndRichEdit);
1485 static int nCallbackCount = 0;
1487 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1488 LONG cb, LONG* pcb)
1490 const char text[] = {'t','e','s','t'};
1492 if (sizeof(text) <= cb)
1494 if ((int)dwCookie != nCallbackCount)
1496 *pcb = 0;
1497 return 0;
1500 memcpy (pbBuff, text, sizeof(text));
1501 *pcb = sizeof(text);
1503 nCallbackCount++;
1505 return 0;
1507 else
1508 return 1; /* indicates callback failed */
1511 static void test_EM_StreamIn_Undo(void)
1513 /* The purpose of this test is to determine when a EM_StreamIn should be
1514 * undoable. This is important because WM_PASTE currently uses StreamIn and
1515 * pasting should always be undoable but streaming isn't always.
1517 * cases to test:
1518 * StreamIn plain text without SFF_SELECTION.
1519 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1520 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1521 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1522 * Feel free to add tests for other text modes or StreamIn things.
1526 HWND hwndRichEdit = new_richedit(NULL);
1527 LRESULT result;
1528 EDITSTREAM es;
1529 char buffer[1024] = {0};
1530 const char randomtext[] = "Some text";
1532 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1534 /* StreamIn, no SFF_SELECTION */
1535 es.dwCookie = nCallbackCount;
1536 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1537 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1538 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1539 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1540 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1541 result = strcmp (buffer,"test");
1542 ok (result == 0,
1543 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1545 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1546 ok (result == FALSE,
1547 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1549 /* StreamIn, SFF_SELECTION, but nothing selected */
1550 es.dwCookie = nCallbackCount;
1551 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1552 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1553 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1554 SendMessage(hwndRichEdit, EM_STREAMIN,
1555 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1556 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1557 result = strcmp (buffer,"testSome text");
1558 ok (result == 0,
1559 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1561 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1562 ok (result == TRUE,
1563 "EM_STREAMIN with SFF_SELECTION but no selection set "
1564 "should create an undo\n");
1566 /* StreamIn, SFF_SELECTION, with a selection */
1567 es.dwCookie = nCallbackCount;
1568 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1569 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1570 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1571 SendMessage(hwndRichEdit, EM_STREAMIN,
1572 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1573 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1574 result = strcmp (buffer,"Sometesttext");
1575 ok (result == 0,
1576 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1578 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1579 ok (result == TRUE,
1580 "EM_STREAMIN with SFF_SELECTION and selection set "
1581 "should create an undo\n");
1585 static BOOL is_em_settextex_supported(HWND hwnd)
1587 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
1588 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
1591 static void test_unicode_conversions(void)
1593 static const WCHAR tW[] = {'t',0};
1594 static const WCHAR teW[] = {'t','e',0};
1595 static const WCHAR textW[] = {'t','e','s','t',0};
1596 static const char textA[] = "test";
1597 char bufA[64];
1598 WCHAR bufW[64];
1599 HWND hwnd;
1600 int is_win9x, em_settextex_supported, ret;
1602 is_win9x = GetVersion() & 0x80000000;
1604 #define set_textA(hwnd, wm_set_text, txt) \
1605 do { \
1606 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1607 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1608 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1609 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1610 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1611 } while(0)
1612 #define expect_textA(hwnd, wm_get_text, txt) \
1613 do { \
1614 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1615 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1616 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1617 memset(bufA, 0xAA, sizeof(bufA)); \
1618 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1619 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1620 ret = lstrcmpA(bufA, txt); \
1621 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1622 } while(0)
1624 #define set_textW(hwnd, wm_set_text, txt) \
1625 do { \
1626 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1627 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1628 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1629 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1630 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1631 } while(0)
1632 #define expect_textW(hwnd, wm_get_text, txt) \
1633 do { \
1634 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1635 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1636 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1637 memset(bufW, 0xAA, sizeof(bufW)); \
1638 if (is_win9x) \
1640 assert(wm_get_text == EM_GETTEXTEX); \
1641 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1642 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1644 else \
1646 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1647 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1649 ret = lstrcmpW(bufW, txt); \
1650 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1651 } while(0)
1652 #define expect_empty(hwnd, wm_get_text) \
1653 do { \
1654 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1655 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1656 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1657 memset(bufA, 0xAA, sizeof(bufA)); \
1658 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1659 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1660 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1661 } while(0)
1663 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1664 0, 0, 200, 60, 0, 0, 0, 0);
1665 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1667 ret = IsWindowUnicode(hwnd);
1668 if (is_win9x)
1669 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1670 else
1671 ok(ret, "RichEdit20W should be unicode under NT\n");
1673 /* EM_SETTEXTEX is supported starting from version 3.0 */
1674 em_settextex_supported = is_em_settextex_supported(hwnd);
1675 trace("EM_SETTEXTEX is %ssupported on this platform\n",
1676 em_settextex_supported ? "" : "NOT ");
1678 expect_empty(hwnd, WM_GETTEXT);
1679 expect_empty(hwnd, EM_GETTEXTEX);
1681 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1682 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1683 expect_textA(hwnd, WM_GETTEXT, "t");
1684 expect_textA(hwnd, EM_GETTEXTEX, "t");
1685 expect_textW(hwnd, EM_GETTEXTEX, tW);
1687 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1688 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1689 expect_textA(hwnd, WM_GETTEXT, "te");
1690 expect_textA(hwnd, EM_GETTEXTEX, "te");
1691 expect_textW(hwnd, EM_GETTEXTEX, teW);
1693 set_textA(hwnd, WM_SETTEXT, NULL);
1694 expect_empty(hwnd, WM_GETTEXT);
1695 expect_empty(hwnd, EM_GETTEXTEX);
1697 if (is_win9x)
1698 set_textA(hwnd, WM_SETTEXT, textW);
1699 else
1700 set_textA(hwnd, WM_SETTEXT, textA);
1701 expect_textA(hwnd, WM_GETTEXT, textA);
1702 expect_textA(hwnd, EM_GETTEXTEX, textA);
1703 expect_textW(hwnd, EM_GETTEXTEX, textW);
1705 if (em_settextex_supported)
1707 set_textA(hwnd, EM_SETTEXTEX, textA);
1708 expect_textA(hwnd, WM_GETTEXT, textA);
1709 expect_textA(hwnd, EM_GETTEXTEX, textA);
1710 expect_textW(hwnd, EM_GETTEXTEX, textW);
1713 if (!is_win9x)
1715 set_textW(hwnd, WM_SETTEXT, textW);
1716 expect_textW(hwnd, WM_GETTEXT, textW);
1717 expect_textA(hwnd, WM_GETTEXT, textA);
1718 expect_textW(hwnd, EM_GETTEXTEX, textW);
1719 expect_textA(hwnd, EM_GETTEXTEX, textA);
1721 if (em_settextex_supported)
1723 set_textW(hwnd, EM_SETTEXTEX, textW);
1724 expect_textW(hwnd, WM_GETTEXT, textW);
1725 expect_textA(hwnd, WM_GETTEXT, textA);
1726 expect_textW(hwnd, EM_GETTEXTEX, textW);
1727 expect_textA(hwnd, EM_GETTEXTEX, textA);
1730 DestroyWindow(hwnd);
1732 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
1733 0, 0, 200, 60, 0, 0, 0, 0);
1734 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1736 ret = IsWindowUnicode(hwnd);
1737 ok(!ret, "RichEdit20A should NOT be unicode\n");
1739 set_textA(hwnd, WM_SETTEXT, textA);
1740 expect_textA(hwnd, WM_GETTEXT, textA);
1741 expect_textA(hwnd, EM_GETTEXTEX, textA);
1742 expect_textW(hwnd, EM_GETTEXTEX, textW);
1744 if (em_settextex_supported)
1746 set_textA(hwnd, EM_SETTEXTEX, textA);
1747 expect_textA(hwnd, WM_GETTEXT, textA);
1748 expect_textA(hwnd, EM_GETTEXTEX, textA);
1749 expect_textW(hwnd, EM_GETTEXTEX, textW);
1752 if (!is_win9x)
1754 set_textW(hwnd, WM_SETTEXT, textW);
1755 expect_textW(hwnd, WM_GETTEXT, textW);
1756 expect_textA(hwnd, WM_GETTEXT, textA);
1757 expect_textW(hwnd, EM_GETTEXTEX, textW);
1758 expect_textA(hwnd, EM_GETTEXTEX, textA);
1760 if (em_settextex_supported)
1762 set_textW(hwnd, EM_SETTEXTEX, textW);
1763 expect_textW(hwnd, WM_GETTEXT, textW);
1764 expect_textA(hwnd, WM_GETTEXT, textA);
1765 expect_textW(hwnd, EM_GETTEXTEX, textW);
1766 expect_textA(hwnd, EM_GETTEXTEX, textA);
1769 DestroyWindow(hwnd);
1772 START_TEST( editor )
1774 MSG msg;
1775 time_t end;
1777 /* Must explicitly LoadLibrary(). The test has no references to functions in
1778 * RICHED20.DLL, so the linker doesn't actually link to it. */
1779 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
1780 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1782 test_EM_FINDTEXT();
1783 test_EM_GETLINE();
1784 test_EM_SCROLLCARET();
1785 test_EM_SCROLL();
1786 test_EM_SETTEXTMODE();
1787 test_TM_PLAINTEXT();
1788 test_EM_SETOPTIONS();
1789 test_WM_GETTEXT();
1790 test_EM_AUTOURLDETECT();
1791 test_EM_SETUNDOLIMIT();
1792 test_ES_PASSWORD();
1793 test_EM_SETTEXTEX();
1794 test_EM_LIMITTEXT();
1795 test_EM_EXLIMITTEXT();
1796 test_EM_GETLIMITTEXT();
1797 test_WM_SETFONT();
1798 test_EM_GETMODIFY();
1799 test_EM_EXSETSEL();
1800 test_WM_PASTE();
1801 test_EM_StreamIn_Undo();
1802 test_unicode_conversions();
1804 /* Set the environment variable WINETEST_RICHED20 to keep windows
1805 * responsive and open for 30 seconds. This is useful for debugging.
1807 * The message pump uses PeekMessage() to empty the queue and then sleeps for
1808 * 50ms before retrying the queue. */
1809 end = time(NULL) + 30;
1810 if (getenv( "WINETEST_RICHED20" )) {
1811 while (time(NULL) < end) {
1812 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1813 TranslateMessage(&msg);
1814 DispatchMessage(&msg);
1815 } else {
1816 Sleep(50);
1821 OleFlushClipboard();
1822 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());