push 27a9569132e9fc3a545aded7efca0a004a7b7ea9
[wine/hacks.git] / dlls / riched20 / tests / editor.c
blob18810057f63d4528025375d969f5f7d4db096ec1
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 int nCallbackCount = 0;
1496 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
1497 LONG cb, LONG* pcb)
1499 const char text[] = {'t','e','s','t'};
1501 if (sizeof(text) <= cb)
1503 if ((int)dwCookie != nCallbackCount)
1505 *pcb = 0;
1506 return 0;
1509 memcpy (pbBuff, text, sizeof(text));
1510 *pcb = sizeof(text);
1512 nCallbackCount++;
1514 return 0;
1516 else
1517 return 1; /* indicates callback failed */
1520 static void test_EM_StreamIn_Undo(void)
1522 /* The purpose of this test is to determine when a EM_StreamIn should be
1523 * undoable. This is important because WM_PASTE currently uses StreamIn and
1524 * pasting should always be undoable but streaming isn't always.
1526 * cases to test:
1527 * StreamIn plain text without SFF_SELECTION.
1528 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
1529 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
1530 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
1531 * Feel free to add tests for other text modes or StreamIn things.
1535 HWND hwndRichEdit = new_richedit(NULL);
1536 LRESULT result;
1537 EDITSTREAM es;
1538 char buffer[1024] = {0};
1539 const char randomtext[] = "Some text";
1541 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
1543 /* StreamIn, no SFF_SELECTION */
1544 es.dwCookie = nCallbackCount;
1545 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1546 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1547 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1548 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
1549 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1550 result = strcmp (buffer,"test");
1551 ok (result == 0,
1552 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
1554 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1555 ok (result == FALSE,
1556 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
1558 /* StreamIn, SFF_SELECTION, but nothing selected */
1559 es.dwCookie = nCallbackCount;
1560 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1561 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1562 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
1563 SendMessage(hwndRichEdit, EM_STREAMIN,
1564 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1565 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1566 result = strcmp (buffer,"testSome text");
1567 ok (result == 0,
1568 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1570 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1571 ok (result == TRUE,
1572 "EM_STREAMIN with SFF_SELECTION but no selection set "
1573 "should create an undo\n");
1575 /* StreamIn, SFF_SELECTION, with a selection */
1576 es.dwCookie = nCallbackCount;
1577 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
1578 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
1579 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
1580 SendMessage(hwndRichEdit, EM_STREAMIN,
1581 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
1582 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1583 result = strcmp (buffer,"Sometesttext");
1584 ok (result == 0,
1585 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
1587 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
1588 ok (result == TRUE,
1589 "EM_STREAMIN with SFF_SELECTION and selection set "
1590 "should create an undo\n");
1594 static BOOL is_em_settextex_supported(HWND hwnd)
1596 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
1597 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
1600 static void test_unicode_conversions(void)
1602 static const WCHAR tW[] = {'t',0};
1603 static const WCHAR teW[] = {'t','e',0};
1604 static const WCHAR textW[] = {'t','e','s','t',0};
1605 static const char textA[] = "test";
1606 char bufA[64];
1607 WCHAR bufW[64];
1608 HWND hwnd;
1609 int is_win9x, em_settextex_supported, ret;
1611 is_win9x = GetVersion() & 0x80000000;
1613 #define set_textA(hwnd, wm_set_text, txt) \
1614 do { \
1615 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
1616 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1617 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1618 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1619 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
1620 } while(0)
1621 #define expect_textA(hwnd, wm_get_text, txt) \
1622 do { \
1623 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1624 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1625 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1626 memset(bufA, 0xAA, sizeof(bufA)); \
1627 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1628 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1629 ret = lstrcmpA(bufA, txt); \
1630 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
1631 } while(0)
1633 #define set_textW(hwnd, wm_set_text, txt) \
1634 do { \
1635 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
1636 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
1637 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
1638 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
1639 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
1640 } while(0)
1641 #define expect_textW(hwnd, wm_get_text, txt) \
1642 do { \
1643 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
1644 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1645 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1646 memset(bufW, 0xAA, sizeof(bufW)); \
1647 if (is_win9x) \
1649 assert(wm_get_text == EM_GETTEXTEX); \
1650 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1651 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
1653 else \
1655 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
1656 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
1658 ret = lstrcmpW(bufW, txt); \
1659 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
1660 } while(0)
1661 #define expect_empty(hwnd, wm_get_text) \
1662 do { \
1663 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
1664 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
1665 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
1666 memset(bufA, 0xAA, sizeof(bufA)); \
1667 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
1668 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
1669 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
1670 } while(0)
1672 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
1673 0, 0, 200, 60, 0, 0, 0, 0);
1674 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1676 ret = IsWindowUnicode(hwnd);
1677 if (is_win9x)
1678 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
1679 else
1680 ok(ret, "RichEdit20W should be unicode under NT\n");
1682 /* EM_SETTEXTEX is supported starting from version 3.0 */
1683 em_settextex_supported = is_em_settextex_supported(hwnd);
1684 trace("EM_SETTEXTEX is %ssupported on this platform\n",
1685 em_settextex_supported ? "" : "NOT ");
1687 expect_empty(hwnd, WM_GETTEXT);
1688 expect_empty(hwnd, EM_GETTEXTEX);
1690 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
1691 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1692 expect_textA(hwnd, WM_GETTEXT, "t");
1693 expect_textA(hwnd, EM_GETTEXTEX, "t");
1694 expect_textW(hwnd, EM_GETTEXTEX, tW);
1696 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
1697 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
1698 expect_textA(hwnd, WM_GETTEXT, "te");
1699 expect_textA(hwnd, EM_GETTEXTEX, "te");
1700 expect_textW(hwnd, EM_GETTEXTEX, teW);
1702 set_textA(hwnd, WM_SETTEXT, NULL);
1703 expect_empty(hwnd, WM_GETTEXT);
1704 expect_empty(hwnd, EM_GETTEXTEX);
1706 if (is_win9x)
1707 set_textA(hwnd, WM_SETTEXT, textW);
1708 else
1709 set_textA(hwnd, WM_SETTEXT, textA);
1710 expect_textA(hwnd, WM_GETTEXT, textA);
1711 expect_textA(hwnd, EM_GETTEXTEX, textA);
1712 expect_textW(hwnd, EM_GETTEXTEX, textW);
1714 if (em_settextex_supported)
1716 set_textA(hwnd, EM_SETTEXTEX, textA);
1717 expect_textA(hwnd, WM_GETTEXT, textA);
1718 expect_textA(hwnd, EM_GETTEXTEX, textA);
1719 expect_textW(hwnd, EM_GETTEXTEX, textW);
1722 if (!is_win9x)
1724 set_textW(hwnd, WM_SETTEXT, textW);
1725 expect_textW(hwnd, WM_GETTEXT, textW);
1726 expect_textA(hwnd, WM_GETTEXT, textA);
1727 expect_textW(hwnd, EM_GETTEXTEX, textW);
1728 expect_textA(hwnd, EM_GETTEXTEX, textA);
1730 if (em_settextex_supported)
1732 set_textW(hwnd, EM_SETTEXTEX, textW);
1733 expect_textW(hwnd, WM_GETTEXT, textW);
1734 expect_textA(hwnd, WM_GETTEXT, textA);
1735 expect_textW(hwnd, EM_GETTEXTEX, textW);
1736 expect_textA(hwnd, EM_GETTEXTEX, textA);
1739 DestroyWindow(hwnd);
1741 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
1742 0, 0, 200, 60, 0, 0, 0, 0);
1743 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
1745 ret = IsWindowUnicode(hwnd);
1746 ok(!ret, "RichEdit20A should NOT be unicode\n");
1748 set_textA(hwnd, WM_SETTEXT, textA);
1749 expect_textA(hwnd, WM_GETTEXT, textA);
1750 expect_textA(hwnd, EM_GETTEXTEX, textA);
1751 expect_textW(hwnd, EM_GETTEXTEX, textW);
1753 if (em_settextex_supported)
1755 set_textA(hwnd, EM_SETTEXTEX, textA);
1756 expect_textA(hwnd, WM_GETTEXT, textA);
1757 expect_textA(hwnd, EM_GETTEXTEX, textA);
1758 expect_textW(hwnd, EM_GETTEXTEX, textW);
1761 if (!is_win9x)
1763 set_textW(hwnd, WM_SETTEXT, textW);
1764 expect_textW(hwnd, WM_GETTEXT, textW);
1765 expect_textA(hwnd, WM_GETTEXT, textA);
1766 expect_textW(hwnd, EM_GETTEXTEX, textW);
1767 expect_textA(hwnd, EM_GETTEXTEX, textA);
1769 if (em_settextex_supported)
1771 set_textW(hwnd, EM_SETTEXTEX, textW);
1772 expect_textW(hwnd, WM_GETTEXT, textW);
1773 expect_textA(hwnd, WM_GETTEXT, textA);
1774 expect_textW(hwnd, EM_GETTEXTEX, textW);
1775 expect_textA(hwnd, EM_GETTEXTEX, textA);
1778 DestroyWindow(hwnd);
1781 START_TEST( editor )
1783 MSG msg;
1784 time_t end;
1786 /* Must explicitly LoadLibrary(). The test has no references to functions in
1787 * RICHED20.DLL, so the linker doesn't actually link to it. */
1788 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
1789 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1791 test_EM_FINDTEXT();
1792 test_EM_GETLINE();
1793 test_EM_SCROLLCARET();
1794 test_EM_SCROLL();
1795 test_EM_SETTEXTMODE();
1796 test_TM_PLAINTEXT();
1797 test_EM_SETOPTIONS();
1798 test_WM_GETTEXT();
1799 test_EM_AUTOURLDETECT();
1800 test_EM_SETUNDOLIMIT();
1801 test_ES_PASSWORD();
1802 test_EM_SETTEXTEX();
1803 test_EM_LIMITTEXT();
1804 test_EM_EXLIMITTEXT();
1805 test_EM_GETLIMITTEXT();
1806 test_WM_SETFONT();
1807 test_EM_GETMODIFY();
1808 test_EM_EXSETSEL();
1809 test_WM_PASTE();
1810 test_EM_StreamIn_Undo();
1811 test_unicode_conversions();
1813 /* Set the environment variable WINETEST_RICHED20 to keep windows
1814 * responsive and open for 30 seconds. This is useful for debugging.
1816 * The message pump uses PeekMessage() to empty the queue and then sleeps for
1817 * 50ms before retrying the queue. */
1818 end = time(NULL) + 30;
1819 if (getenv( "WINETEST_RICHED20" )) {
1820 while (time(NULL) < end) {
1821 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1822 TranslateMessage(&msg);
1823 DispatchMessage(&msg);
1824 } else {
1825 Sleep(50);
1830 OleFlushClipboard();
1831 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());