richedit: Revert patch that introduced a test that fails on all platforms.
[wine/wine64.git] / dlls / riched20 / tests / editor.c
blobbbdba4dce564278c436d17cdd04cdc422650a3f0
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},
132 {0, -1, "wineWine wine", 0, -1, 0},
135 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
136 int findloc;
137 FINDTEXT ft;
138 memset(&ft, 0, sizeof(ft));
139 ft.chrg.cpMin = f->start;
140 ft.chrg.cpMax = f->end;
141 ft.lpstrText = f->needle;
142 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
143 ok(findloc == f->expected_loc,
144 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
145 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
148 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
149 int id) {
150 int findloc;
151 FINDTEXTEX ft;
152 int expected_end_loc;
154 memset(&ft, 0, sizeof(ft));
155 ft.chrg.cpMin = f->start;
156 ft.chrg.cpMax = f->end;
157 ft.lpstrText = f->needle;
158 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
159 ok(findloc == f->expected_loc,
160 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
161 name, id, f->needle, f->start, f->end, f->flags, findloc);
162 ok(ft.chrgText.cpMin == f->expected_loc,
163 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
164 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
165 expected_end_loc = ((f->expected_loc == -1) ? -1
166 : f->expected_loc + strlen(f->needle));
167 ok(ft.chrgText.cpMax == expected_end_loc,
168 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
169 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
172 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
173 int num_tests)
175 int i;
177 for (i = 0; i < num_tests; i++) {
178 if (find[i]._todo_wine) {
179 todo_wine {
180 check_EM_FINDTEXT(hwnd, name, &find[i], i);
181 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
183 } else {
184 check_EM_FINDTEXT(hwnd, name, &find[i], i);
185 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
190 static void test_EM_FINDTEXT(void)
192 HWND hwndRichEdit = new_richedit(NULL);
193 CHARFORMAT2 cf2;
195 /* Empty rich edit control */
196 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
197 sizeof(find_tests)/sizeof(struct find_s));
199 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
201 /* Haystack text */
202 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
203 sizeof(find_tests2)/sizeof(struct find_s));
205 /* Setting a format on an arbitrary range should have no effect in search
206 results. This tests correct offset reporting across runs. */
207 cf2.cbSize = sizeof(CHARFORMAT2);
208 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
209 (LPARAM) &cf2);
210 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
211 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
212 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
213 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
215 /* Haystack text, again */
216 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
217 sizeof(find_tests2)/sizeof(struct find_s));
219 /* Yet another range */
220 cf2.dwMask = CFM_BOLD | cf2.dwMask;
221 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
222 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
223 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
225 /* Haystack text, again */
226 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
227 sizeof(find_tests2)/sizeof(struct find_s));
229 DestroyWindow(hwndRichEdit);
232 static const struct getline_s {
233 int line;
234 size_t buffer_len;
235 const char *text;
236 } gl[] = {
237 {0, 10, "foo bar\r"},
238 {1, 10, "\r"},
239 {2, 10, "bar\r"},
240 {3, 10, "\r"},
242 /* Buffer smaller than line length */
243 {0, 2, "foo bar\r"},
244 {0, 1, "foo bar\r"},
245 {0, 0, "foo bar\r"}
248 static void test_EM_GETLINE(void)
250 int i;
251 HWND hwndRichEdit = new_richedit(NULL);
252 static const int nBuf = 1024;
253 char dest[1024], origdest[1024];
254 const char text[] = "foo bar\n"
255 "\n"
256 "bar\n";
258 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
260 memset(origdest, 0xBB, nBuf);
261 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
263 int nCopied;
264 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
265 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
266 memset(dest, 0xBB, nBuf);
267 *(WORD *) dest = gl[i].buffer_len;
269 /* EM_GETLINE appends a "\r\0" to the end of the line
270 * nCopied counts up to and including the '\r' */
271 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
272 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
273 expected_nCopied);
274 /* two special cases since a parameter is passed via dest */
275 if (gl[i].buffer_len == 0)
276 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
277 "buffer_len=0\n");
278 else if (gl[i].buffer_len == 1)
279 ok(dest[0] == gl[i].text[0] && !dest[1] &&
280 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
281 else
283 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
284 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
285 ok(!strncmp(dest + expected_bytes_written, origdest
286 + expected_bytes_written, nBuf - expected_bytes_written),
287 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
291 DestroyWindow(hwndRichEdit);
294 static void test_EM_LINELENGTH(void)
296 HWND hwndRichEdit = new_richedit(NULL);
297 const char * text =
298 "richedit1\r"
299 "richedit1\n"
300 "richedit1\r\n"
301 "richedit1";
302 int offset_test[10][2] = {
303 {0, 9},
304 {5, 9},
305 {10, 9},
306 {15, 9},
307 {20, 9},
308 {25, 9},
309 {30, 9},
310 {35, 9},
311 {40, 0},
312 {45, 0},
314 int i;
315 LRESULT result;
317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
319 for (i = 0; i < 10; i++) {
320 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
321 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
322 offset_test[i][0], result, offset_test[i][1]);
325 DestroyWindow(hwndRichEdit);
328 static int get_scroll_pos_y(HWND hwnd)
330 POINT p = {-1, -1};
331 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
332 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
333 return p.y;
336 static void move_cursor(HWND hwnd, long charindex)
338 CHARRANGE cr;
339 cr.cpMax = charindex;
340 cr.cpMin = charindex;
341 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
344 static void line_scroll(HWND hwnd, int amount)
346 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
349 static void test_EM_SCROLLCARET(void)
351 int prevY, curY;
352 HWND hwndRichEdit = new_richedit(NULL);
353 const char text[] = "aa\n"
354 "this is a long line of text that should be longer than the "
355 "control's width\n"
356 "cc\n"
357 "dd\n"
358 "ee\n"
359 "ff\n"
360 "gg\n"
361 "hh\n";
363 /* Can't verify this */
364 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
366 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
368 /* Caret above visible window */
369 line_scroll(hwndRichEdit, 3);
370 prevY = get_scroll_pos_y(hwndRichEdit);
371 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
372 curY = get_scroll_pos_y(hwndRichEdit);
373 ok(prevY != curY, "%d == %d\n", prevY, curY);
375 /* Caret below visible window */
376 move_cursor(hwndRichEdit, sizeof(text) - 1);
377 line_scroll(hwndRichEdit, -3);
378 prevY = get_scroll_pos_y(hwndRichEdit);
379 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
380 curY = get_scroll_pos_y(hwndRichEdit);
381 ok(prevY != curY, "%d == %d\n", prevY, curY);
383 /* Caret in visible window */
384 move_cursor(hwndRichEdit, sizeof(text) - 2);
385 prevY = get_scroll_pos_y(hwndRichEdit);
386 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
387 curY = get_scroll_pos_y(hwndRichEdit);
388 ok(prevY == curY, "%d != %d\n", prevY, curY);
390 /* Caret still in visible window */
391 line_scroll(hwndRichEdit, -1);
392 prevY = get_scroll_pos_y(hwndRichEdit);
393 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
394 curY = get_scroll_pos_y(hwndRichEdit);
395 ok(prevY == curY, "%d != %d\n", prevY, curY);
397 DestroyWindow(hwndRichEdit);
400 static void test_EM_POSFROMCHAR(void)
402 HWND hwndRichEdit = new_richedit(NULL);
403 int i;
404 LRESULT result;
405 unsigned int height = 0;
406 int xpos = 0;
407 static const char text[] = "aa\n"
408 "this is a long line of text that should be longer than the "
409 "control's width\n"
410 "cc\n"
411 "dd\n"
412 "ee\n"
413 "ff\n"
414 "gg\n"
415 "hh\n";
417 /* Fill the control to lines to ensure that most of them are offscreen */
418 for (i = 0; i < 50; i++)
420 /* Do not modify the string; it is exactly 16 characters long. */
421 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
422 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
426 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
427 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
428 Richedit 3.0 accepts either of the above API conventions.
431 /* Testing Richedit 2.0 API format */
433 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
434 Since all lines are identical and drawn with the same font,
435 they should have the same height... right?
437 for (i = 0; i < 50; i++)
439 /* All the lines are 16 characters long */
440 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
441 if (i == 0)
443 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
444 todo_wine {
445 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
447 xpos = LOWORD(result);
449 else if (i == 1)
451 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
452 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
453 height = HIWORD(result);
455 else
457 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
458 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
462 /* Testing position at end of text */
463 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
464 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
465 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
467 /* Testing position way past end of text */
468 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
469 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
470 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
472 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
473 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
474 for (i = 0; i < 50; i++)
476 /* All the lines are 16 characters long */
477 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
478 ok((signed short)(HIWORD(result)) == (i - 1) * height,
479 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
480 (signed short)(HIWORD(result)), (i - 1) * height);
481 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
484 /* Testing position at end of text */
485 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
486 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
487 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
489 /* Testing position way past end of text */
490 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
491 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
492 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
494 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
495 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
496 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
498 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
499 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
500 todo_wine {
501 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
503 xpos = LOWORD(result);
505 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
506 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
507 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
508 todo_wine {
509 /* Fails on builtin because horizontal scrollbar is not being shown */
510 ok((signed short)(LOWORD(result)) < xpos,
511 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
512 (signed short)(LOWORD(result)), xpos);
514 DestroyWindow(hwndRichEdit);
517 static void test_EM_SETCHARFORMAT(void)
519 HWND hwndRichEdit = new_richedit(NULL);
520 CHARFORMAT2 cf2;
521 int rc = 0;
522 int tested_effects[] = {
523 CFE_BOLD,
524 CFE_ITALIC,
525 CFE_UNDERLINE,
526 CFE_STRIKEOUT,
527 CFE_PROTECTED,
528 CFE_LINK,
529 CFE_SUBSCRIPT,
530 CFE_SUPERSCRIPT,
533 int i;
534 CHARRANGE cr;
536 /* Invalid flags, CHARFORMAT2 structure blanked out */
537 memset(&cf2, 0, sizeof(cf2));
538 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
539 (LPARAM) &cf2);
540 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
542 /* A valid flag, CHARFORMAT2 structure blanked out */
543 memset(&cf2, 0, sizeof(cf2));
544 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
545 (LPARAM) &cf2);
546 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
548 /* A valid flag, CHARFORMAT2 structure blanked out */
549 memset(&cf2, 0, sizeof(cf2));
550 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
551 (LPARAM) &cf2);
552 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
554 /* A valid flag, CHARFORMAT2 structure blanked out */
555 memset(&cf2, 0, sizeof(cf2));
556 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
557 (LPARAM) &cf2);
558 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
560 /* A valid flag, CHARFORMAT2 structure blanked out */
561 memset(&cf2, 0, sizeof(cf2));
562 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
563 (LPARAM) &cf2);
564 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
566 /* Invalid flags, CHARFORMAT2 structure minimally filled */
567 memset(&cf2, 0, sizeof(cf2));
568 cf2.cbSize = sizeof(CHARFORMAT2);
569 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
570 (LPARAM) &cf2);
571 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
573 /* A valid flag, CHARFORMAT2 structure minimally filled */
574 memset(&cf2, 0, sizeof(cf2));
575 cf2.cbSize = sizeof(CHARFORMAT2);
576 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
577 (LPARAM) &cf2);
578 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
580 /* A valid flag, CHARFORMAT2 structure minimally filled */
581 memset(&cf2, 0, sizeof(cf2));
582 cf2.cbSize = sizeof(CHARFORMAT2);
583 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
584 (LPARAM) &cf2);
585 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
587 /* A valid flag, CHARFORMAT2 structure minimally filled */
588 memset(&cf2, 0, sizeof(cf2));
589 cf2.cbSize = sizeof(CHARFORMAT2);
590 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
591 (LPARAM) &cf2);
592 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
594 /* A valid flag, CHARFORMAT2 structure minimally filled */
595 memset(&cf2, 0, sizeof(cf2));
596 cf2.cbSize = sizeof(CHARFORMAT2);
597 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
598 (LPARAM) &cf2);
599 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
601 cf2.cbSize = sizeof(CHARFORMAT2);
602 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
603 (LPARAM) &cf2);
605 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
606 cf2.cbSize = sizeof(CHARFORMAT2);
607 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
608 (LPARAM) &cf2);
609 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
610 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
612 /* wParam==0 is default char format, does not set modify */
613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
614 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
615 ok(rc == 0, "Text marked as modified, expected not modified!\n");
616 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
617 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
618 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
619 ok(rc == 0, "Text marked as modified, expected not modified!\n");
621 /* wParam==SCF_SELECTION sets modify if nonempty selection */
622 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
623 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
624 ok(rc == 0, "Text marked as modified, expected not modified!\n");
625 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
626 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
627 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
628 ok(rc == 0, "Text marked as modified, expected not modified!\n");
630 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
631 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
632 ok(rc == 0, "Text marked as modified, expected not modified!\n");
633 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
634 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
635 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
636 ok(rc == 0, "Text marked as modified, expected not modified!\n");
637 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
638 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
639 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
640 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
641 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
643 /* wParam==SCF_ALL sets modify regardless of whether text is present */
644 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
645 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
646 ok(rc == 0, "Text marked as modified, expected not modified!\n");
647 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
648 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
649 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
650 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
652 DestroyWindow(hwndRichEdit);
654 /* EM_GETCHARFORMAT tests */
655 for (i = 0; tested_effects[i]; i++)
657 hwndRichEdit = new_richedit(NULL);
658 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
660 memset(&cf2, 0, sizeof(CHARFORMAT2));
661 cf2.cbSize = sizeof(CHARFORMAT2);
662 cf2.dwMask = tested_effects[i];
663 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
664 cf2.dwMask = CFM_SUPERSCRIPT;
665 cf2.dwEffects = tested_effects[i];
666 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
667 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
669 memset(&cf2, 0, sizeof(CHARFORMAT2));
670 cf2.cbSize = sizeof(CHARFORMAT2);
671 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
672 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
673 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
674 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
676 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
677 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
678 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
679 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
681 memset(&cf2, 0, sizeof(CHARFORMAT2));
682 cf2.cbSize = sizeof(CHARFORMAT2);
683 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
684 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
685 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
686 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
688 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
689 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
690 ok((cf2.dwEffects & tested_effects[i]) == 0,
691 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
693 memset(&cf2, 0, sizeof(CHARFORMAT2));
694 cf2.cbSize = sizeof(CHARFORMAT2);
695 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
696 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
697 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
698 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
700 (cf2.dwMask & tested_effects[i]) == 0),
701 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
702 ok((cf2.dwEffects & tested_effects[i]) == 0,
703 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
705 DestroyWindow(hwndRichEdit);
708 for (i = 0; tested_effects[i]; i++)
710 hwndRichEdit = new_richedit(NULL);
711 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
713 memset(&cf2, 0, sizeof(CHARFORMAT2));
714 cf2.cbSize = sizeof(CHARFORMAT2);
715 cf2.dwMask = tested_effects[i];
716 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
717 cf2.dwMask = CFM_SUPERSCRIPT;
718 cf2.dwEffects = tested_effects[i];
719 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
720 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
722 memset(&cf2, 0, sizeof(CHARFORMAT2));
723 cf2.cbSize = sizeof(CHARFORMAT2);
724 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
725 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
726 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
727 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
729 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
730 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
731 ok((cf2.dwEffects & tested_effects[i]) == 0,
732 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
734 memset(&cf2, 0, sizeof(CHARFORMAT2));
735 cf2.cbSize = sizeof(CHARFORMAT2);
736 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
737 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
738 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
739 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
741 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
742 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
743 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
744 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
746 memset(&cf2, 0, sizeof(CHARFORMAT2));
747 cf2.cbSize = sizeof(CHARFORMAT2);
748 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
749 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
750 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
751 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
753 (cf2.dwMask & tested_effects[i]) == 0),
754 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
755 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
756 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
758 DestroyWindow(hwndRichEdit);
761 /* Effects applied on an empty selection should take effect when selection is
762 replaced with text */
763 hwndRichEdit = new_richedit(NULL);
764 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
765 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
767 memset(&cf2, 0, sizeof(CHARFORMAT2));
768 cf2.cbSize = sizeof(CHARFORMAT2);
769 cf2.dwMask = CFM_BOLD;
770 cf2.dwEffects = CFE_BOLD;
771 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
773 /* Selection is now nonempty */
774 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
776 memset(&cf2, 0, sizeof(CHARFORMAT2));
777 cf2.cbSize = sizeof(CHARFORMAT2);
778 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
779 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
781 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
782 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
783 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
784 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
787 /* Set two effects on an empty selection */
788 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
789 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
791 memset(&cf2, 0, sizeof(CHARFORMAT2));
792 cf2.cbSize = sizeof(CHARFORMAT2);
793 cf2.dwMask = CFM_BOLD;
794 cf2.dwEffects = CFE_BOLD;
795 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
796 cf2.dwMask = CFM_ITALIC;
797 cf2.dwEffects = CFE_ITALIC;
798 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
800 /* Selection is now nonempty */
801 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
803 memset(&cf2, 0, sizeof(CHARFORMAT2));
804 cf2.cbSize = sizeof(CHARFORMAT2);
805 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
806 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
808 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
809 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
810 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
811 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
813 /* Setting the (empty) selection to exactly the same place as before should
814 NOT clear the insertion style! */
815 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
816 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
818 memset(&cf2, 0, sizeof(CHARFORMAT2));
819 cf2.cbSize = sizeof(CHARFORMAT2);
820 cf2.dwMask = CFM_BOLD;
821 cf2.dwEffects = CFE_BOLD;
822 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
824 /* Empty selection in same place, insert style should NOT be forgotten here. */
825 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
827 /* Selection is now nonempty */
828 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
830 memset(&cf2, 0, sizeof(CHARFORMAT2));
831 cf2.cbSize = sizeof(CHARFORMAT2);
832 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
833 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
835 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
836 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
837 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
838 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
840 /* Ditto with EM_EXSETSEL */
841 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
842 cr.cpMin = 2; cr.cpMax = 2;
843 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
845 memset(&cf2, 0, sizeof(CHARFORMAT2));
846 cf2.cbSize = sizeof(CHARFORMAT2);
847 cf2.dwMask = CFM_BOLD;
848 cf2.dwEffects = CFE_BOLD;
849 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
851 /* Empty selection in same place, insert style should NOT be forgotten here. */
852 cr.cpMin = 2; cr.cpMax = 2;
853 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
855 /* Selection is now nonempty */
856 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
858 memset(&cf2, 0, sizeof(CHARFORMAT2));
859 cf2.cbSize = sizeof(CHARFORMAT2);
860 cr.cpMin = 2; cr.cpMax = 6;
861 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
862 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
864 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
865 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
866 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
867 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
869 DestroyWindow(hwndRichEdit);
872 static void test_EM_SETTEXTMODE(void)
874 HWND hwndRichEdit = new_richedit(NULL);
875 CHARFORMAT2 cf2, cf2test;
876 CHARRANGE cr;
877 int rc = 0;
879 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
880 /*Insert text into the control*/
882 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
884 /*Attempt to change the control to plain text mode*/
885 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
886 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
888 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
889 If rich text is pasted, it should have the same formatting as the rest
890 of the text in the control*/
892 /*Italicize the text
893 *NOTE: If the default text was already italicized, the test will simply
894 reverse; in other words, it will copy a regular "wine" into a plain
895 text window that uses an italicized format*/
896 cf2.cbSize = sizeof(CHARFORMAT2);
897 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
898 (LPARAM) &cf2);
900 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
901 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
903 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
904 ok(rc == 0, "Text marked as modified, expected not modified!\n");
906 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
907 however, SCF_ALL has been implemented*/
908 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
909 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
911 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
912 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
914 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
916 /*Select the string "wine"*/
917 cr.cpMin = 0;
918 cr.cpMax = 4;
919 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
921 /*Copy the italicized "wine" to the clipboard*/
922 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
924 /*Reset the formatting to default*/
925 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
926 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
927 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
929 /*Clear the text in the control*/
930 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
932 /*Switch to Plain Text Mode*/
933 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
934 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
936 /*Input "wine" again in normal format*/
937 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
939 /*Paste the italicized "wine" into the control*/
940 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
942 /*Select a character from the first "wine" string*/
943 cr.cpMin = 2;
944 cr.cpMax = 3;
945 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
947 /*Retrieve its formatting*/
948 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
949 (LPARAM) &cf2);
951 /*Select a character from the second "wine" string*/
952 cr.cpMin = 5;
953 cr.cpMax = 6;
954 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
956 /*Retrieve its formatting*/
957 cf2test.cbSize = sizeof(CHARFORMAT2);
958 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
959 (LPARAM) &cf2test);
961 /*Compare the two formattings*/
962 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
963 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
964 cf2.dwEffects, cf2test.dwEffects);
965 /*Test TM_RICHTEXT by: switching back to Rich Text mode
966 printing "wine" in the current format(normal)
967 pasting "wine" from the clipboard(italicized)
968 comparing the two formats(should differ)*/
970 /*Attempt to switch with text in control*/
971 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
972 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
974 /*Clear control*/
975 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
977 /*Switch into Rich Text mode*/
978 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
979 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
981 /*Print "wine" in normal formatting into the control*/
982 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
984 /*Paste italicized "wine" into the control*/
985 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
987 /*Select text from the first "wine" string*/
988 cr.cpMin = 1;
989 cr.cpMax = 3;
990 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
992 /*Retrieve its formatting*/
993 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
994 (LPARAM) &cf2);
996 /*Select text from the second "wine" string*/
997 cr.cpMin = 6;
998 cr.cpMax = 7;
999 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1001 /*Retrieve its formatting*/
1002 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1003 (LPARAM) &cf2test);
1005 /*Test that the two formattings are not the same*/
1006 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1007 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1008 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1010 DestroyWindow(hwndRichEdit);
1013 static void test_TM_PLAINTEXT(void)
1015 /*Tests plain text properties*/
1017 HWND hwndRichEdit = new_richedit(NULL);
1018 CHARFORMAT2 cf2, cf2test;
1019 CHARRANGE cr;
1020 int rc = 0;
1022 /*Switch to plain text mode*/
1024 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1025 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1027 /*Fill control with text*/
1029 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1031 /*Select some text and bold it*/
1033 cr.cpMin = 10;
1034 cr.cpMax = 20;
1035 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1036 cf2.cbSize = sizeof(CHARFORMAT2);
1037 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1038 (LPARAM) &cf2);
1040 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1041 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1043 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1044 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1046 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1047 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1049 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1050 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1052 /*Get the formatting of those characters*/
1054 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1056 /*Get the formatting of some other characters*/
1057 cf2test.cbSize = sizeof(CHARFORMAT2);
1058 cr.cpMin = 21;
1059 cr.cpMax = 30;
1060 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1061 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1063 /*Test that they are the same as plain text allows only one formatting*/
1065 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1066 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1067 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1069 /*Fill the control with a "wine" string, which when inserted will be bold*/
1071 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1073 /*Copy the bolded "wine" string*/
1075 cr.cpMin = 0;
1076 cr.cpMax = 4;
1077 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1078 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1080 /*Swap back to rich text*/
1082 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1083 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1085 /*Set the default formatting to bold italics*/
1087 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1088 cf2.dwMask |= CFM_ITALIC;
1089 cf2.dwEffects ^= CFE_ITALIC;
1090 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1091 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1093 /*Set the text in the control to "wine", which will be bold and italicized*/
1095 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1097 /*Paste the plain text "wine" string, which should take the insert
1098 formatting, which at the moment is bold italics*/
1100 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1102 /*Select the first "wine" string and retrieve its formatting*/
1104 cr.cpMin = 1;
1105 cr.cpMax = 3;
1106 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1107 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1109 /*Select the second "wine" string and retrieve its formatting*/
1111 cr.cpMin = 5;
1112 cr.cpMax = 7;
1113 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1114 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1116 /*Compare the two formattings. They should be the same.*/
1118 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1119 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1120 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1121 DestroyWindow(hwndRichEdit);
1124 static void test_WM_GETTEXT(void)
1126 HWND hwndRichEdit = new_richedit(NULL);
1127 static const char text[] = "Hello. My name is RichEdit!";
1128 static const char text2[] = "Hello. My name is RichEdit!\r";
1129 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1130 char buffer[1024] = {0};
1131 int result;
1133 /* Baseline test with normal-sized buffer */
1134 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1135 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1136 ok(result == lstrlen(buffer),
1137 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1138 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1139 result = strcmp(buffer,text);
1140 ok(result == 0,
1141 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1143 /* Test for returned value of WM_GETTEXTLENGTH */
1144 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1145 ok(result == lstrlen(text),
1146 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1147 result, lstrlen(text));
1149 /* Test for behavior in overflow case */
1150 memset(buffer, 0, 1024);
1151 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1152 ok(result == 0 ||
1153 result == lstrlenA(text) - 1, /* XP, win2k3 */
1154 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1155 result = strcmp(buffer,text);
1156 if (result)
1157 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1158 ok(result == 0,
1159 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1161 /* Baseline test with normal-sized buffer and carriage return */
1162 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1163 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1164 ok(result == lstrlen(buffer),
1165 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1166 result = strcmp(buffer,text2_after);
1167 ok(result == 0,
1168 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1170 /* Test for returned value of WM_GETTEXTLENGTH */
1171 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1172 ok(result == lstrlen(text2_after),
1173 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1174 result, lstrlen(text2_after));
1176 /* Test for behavior of CRLF conversion in case of overflow */
1177 memset(buffer, 0, 1024);
1178 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1179 ok(result == 0 ||
1180 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1181 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1182 result = strcmp(buffer,text2);
1183 if (result)
1184 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1185 ok(result == 0,
1186 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1188 DestroyWindow(hwndRichEdit);
1191 static void test_EM_GETTEXTRANGE(void)
1193 HWND hwndRichEdit = new_richedit(NULL);
1194 const char * text1 = "foo bar\r\nfoo bar";
1195 const char * text2 = "foo bar\rfoo bar";
1196 const char * expect = "bar\rfoo";
1197 char buffer[1024] = {0};
1198 LRESULT result;
1199 TEXTRANGEA textRange;
1201 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1203 textRange.lpstrText = buffer;
1204 textRange.chrg.cpMin = 4;
1205 textRange.chrg.cpMax = 11;
1206 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1207 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1208 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1210 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1212 textRange.lpstrText = buffer;
1213 textRange.chrg.cpMin = 4;
1214 textRange.chrg.cpMax = 11;
1215 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1216 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1217 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1219 DestroyWindow(hwndRichEdit);
1222 static void test_EM_GETSELTEXT(void)
1224 HWND hwndRichEdit = new_richedit(NULL);
1225 const char * text1 = "foo bar\r\nfoo bar";
1226 const char * text2 = "foo bar\rfoo bar";
1227 const char * expect = "bar\rfoo";
1228 char buffer[1024] = {0};
1229 LRESULT result;
1231 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1233 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1234 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1235 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1236 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1238 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1240 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1241 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1242 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1243 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1245 DestroyWindow(hwndRichEdit);
1248 /* FIXME: need to test unimplemented options and robustly test wparam */
1249 static void test_EM_SETOPTIONS(void)
1251 HWND hwndRichEdit = new_richedit(NULL);
1252 static const char text[] = "Hello. My name is RichEdit!";
1253 char buffer[1024] = {0};
1255 /* NEGATIVE TESTING - NO OPTIONS SET */
1256 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1257 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1259 /* testing no readonly by sending 'a' to the control*/
1260 SetFocus(hwndRichEdit);
1261 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1262 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1263 ok(buffer[0]=='a',
1264 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1265 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1267 /* READONLY - sending 'a' to the control */
1268 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1269 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1270 SetFocus(hwndRichEdit);
1271 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1272 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1273 ok(buffer[0]==text[0],
1274 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1276 DestroyWindow(hwndRichEdit);
1279 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1281 CHARFORMAT2W text_format;
1282 text_format.cbSize = sizeof(text_format);
1283 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1284 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1285 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1288 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1290 int link_present = 0;
1292 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1293 if (is_url)
1294 { /* control text is url; should get CFE_LINK */
1295 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1297 else
1299 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1303 static HWND new_static_wnd(HWND parent) {
1304 return new_window("Static", 0, parent);
1307 static void test_EM_AUTOURLDETECT(void)
1309 struct urls_s {
1310 const char *text;
1311 int is_url;
1312 } urls[12] = {
1313 {"winehq.org", 0},
1314 {"http://www.winehq.org", 1},
1315 {"http//winehq.org", 0},
1316 {"ww.winehq.org", 0},
1317 {"www.winehq.org", 1},
1318 {"ftp://192.168.1.1", 1},
1319 {"ftp//192.168.1.1", 0},
1320 {"mailto:your@email.com", 1},
1321 {"prospero:prosperoserver", 1},
1322 {"telnet:test", 1},
1323 {"news:newserver", 1},
1324 {"wais:waisserver", 1}
1327 int i, j;
1328 int urlRet=-1;
1329 HWND hwndRichEdit, parent;
1331 /* All of the following should cause the URL to be detected */
1332 const char * templates_delim[] = {
1333 "This is some text with X on it",
1334 "This is some text with (X) on it",
1335 "This is some text with X\r on it",
1336 "This is some text with ---X--- on it",
1337 "This is some text with \"X\" on it",
1338 "This is some text with 'X' on it",
1339 "This is some text with 'X' on it",
1340 "This is some text with :X: on it",
1342 "This text ends with X",
1344 "This is some text with X) on it",
1345 "This is some text with X--- on it",
1346 "This is some text with X\" on it",
1347 "This is some text with X' on it",
1348 "This is some text with X: on it",
1350 "This is some text with (X on it",
1351 "This is some text with \rX on it",
1352 "This is some text with ---X on it",
1353 "This is some text with \"X on it",
1354 "This is some text with 'X on it",
1355 "This is some text with :X on it",
1357 /* None of these should cause the URL to be detected */
1358 const char * templates_non_delim[] = {
1359 "This is some text with |X| on it",
1360 "This is some text with *X* on it",
1361 "This is some text with /X/ on it",
1362 "This is some text with +X+ on it",
1363 "This is some text with %X% on it",
1364 "This is some text with #X# on it",
1365 "This is some text with @X@ on it",
1366 "This is some text with \\X\\ on it",
1367 "This is some text with |X on it",
1368 "This is some text with *X on it",
1369 "This is some text with /X on it",
1370 "This is some text with +X on it",
1371 "This is some text with %X on it",
1372 "This is some text with #X on it",
1373 "This is some text with @X on it",
1374 "This is some text with \\X on it",
1376 /* All of these cause the URL detection to be extended by one more byte,
1377 thus demonstrating that the tested character is considered as part
1378 of the URL. */
1379 const char * templates_xten_delim[] = {
1380 "This is some text with X| on it",
1381 "This is some text with X* on it",
1382 "This is some text with X/ on it",
1383 "This is some text with X+ on it",
1384 "This is some text with X% on it",
1385 "This is some text with X# on it",
1386 "This is some text with X@ on it",
1387 "This is some text with X\\ on it",
1389 char buffer[1024];
1390 MSG msg;
1392 parent = new_static_wnd(NULL);
1393 hwndRichEdit = new_richedit(parent);
1394 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1395 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1396 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1397 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1398 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1399 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1400 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1401 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1402 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1403 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1404 /* for each url, check the text to see if CFE_LINK effect is present */
1405 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1407 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1408 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1409 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1411 /* Link detection should happen immediately upon WM_SETTEXT */
1412 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1413 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1414 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1416 DestroyWindow(hwndRichEdit);
1418 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1419 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1420 hwndRichEdit = new_richedit(parent);
1422 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1423 char * at_pos;
1424 int at_offset;
1425 int end_offset;
1427 at_pos = strchr(templates_delim[j], 'X');
1428 at_offset = at_pos - templates_delim[j];
1429 strncpy(buffer, templates_delim[j], at_offset);
1430 buffer[at_offset] = '\0';
1431 strcat(buffer, urls[i].text);
1432 strcat(buffer, templates_delim[j] + at_offset + 1);
1433 end_offset = at_offset + strlen(urls[i].text);
1435 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1436 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1438 /* This assumes no templates start with the URL itself, and that they
1439 have at least two characters before the URL text */
1440 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1441 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1442 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1443 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1444 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1445 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1447 if (urls[i].is_url)
1449 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1450 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1451 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1452 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1454 else
1456 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1457 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1458 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1459 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1461 if (buffer[end_offset] != '\0')
1463 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1464 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1465 if (buffer[end_offset +1] != '\0')
1467 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1468 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1473 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1474 char * at_pos;
1475 int at_offset;
1476 int end_offset;
1478 at_pos = strchr(templates_non_delim[j], 'X');
1479 at_offset = at_pos - templates_non_delim[j];
1480 strncpy(buffer, templates_non_delim[j], at_offset);
1481 buffer[at_offset] = '\0';
1482 strcat(buffer, urls[i].text);
1483 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1484 end_offset = at_offset + strlen(urls[i].text);
1486 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1487 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1489 /* This assumes no templates start with the URL itself, and that they
1490 have at least two characters before the URL text */
1491 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1492 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1493 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1494 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1495 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1496 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1498 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1499 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1500 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1501 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1502 if (buffer[end_offset] != '\0')
1504 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1505 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1506 if (buffer[end_offset +1] != '\0')
1508 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1509 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1514 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1515 char * at_pos;
1516 int at_offset;
1517 int end_offset;
1519 at_pos = strchr(templates_xten_delim[j], 'X');
1520 at_offset = at_pos - templates_xten_delim[j];
1521 strncpy(buffer, templates_xten_delim[j], at_offset);
1522 buffer[at_offset] = '\0';
1523 strcat(buffer, urls[i].text);
1524 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1525 end_offset = at_offset + strlen(urls[i].text);
1527 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1528 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1530 /* This assumes no templates start with the URL itself, and that they
1531 have at least two characters before the URL text */
1532 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1533 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1534 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1535 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1536 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1537 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1539 if (urls[i].is_url)
1541 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1542 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1543 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1544 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1545 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1546 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1548 else
1550 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1551 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1552 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1553 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1554 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1555 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1557 if (buffer[end_offset +1] != '\0')
1559 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1560 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1561 if (buffer[end_offset +2] != '\0')
1563 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1564 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1569 DestroyWindow(hwndRichEdit);
1570 hwndRichEdit = NULL;
1573 #define INSERT_CR \
1574 do { \
1575 keybd_event('\r', 0x1c, 0, 0); \
1576 keybd_event('\r', 0x1c, KEYEVENTF_KEYUP, 0); \
1577 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
1578 TranslateMessage(&msg); \
1579 DispatchMessage(&msg); \
1581 } while (0)
1583 #define INSERT_BS \
1584 do { \
1585 keybd_event(0x08, 0x0e, 0, 0); \
1586 keybd_event(0x08, 0x0e, KEYEVENTF_KEYUP, 0); \
1587 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
1588 TranslateMessage(&msg); \
1589 DispatchMessage(&msg); \
1591 } while (0)
1593 /* Test detection of URLs within normal text - WM_CHAR case. */
1594 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1595 hwndRichEdit = new_richedit(parent);
1597 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1598 char * at_pos;
1599 int at_offset;
1600 int end_offset;
1601 int u, v;
1603 at_pos = strchr(templates_delim[j], 'X');
1604 at_offset = at_pos - templates_delim[j];
1605 end_offset = at_offset + strlen(urls[i].text);
1607 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1608 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1609 for (u = 0; templates_delim[j][u]; u++) {
1610 if (templates_delim[j][u] == '\r') {
1611 INSERT_CR;
1612 } else if (templates_delim[j][u] != 'X') {
1613 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1614 } else {
1615 for (v = 0; urls[i].text[v]; v++) {
1616 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1620 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1621 trace("Using template: %s\n", templates_delim[j]);
1623 /* This assumes no templates start with the URL itself, and that they
1624 have at least two characters before the URL text */
1625 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1626 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1627 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1628 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1629 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1630 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1632 if (urls[i].is_url)
1634 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1635 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1636 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1637 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1639 else
1641 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1642 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1643 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1644 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1646 if (buffer[end_offset] != '\0')
1648 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1649 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1650 if (buffer[end_offset +1] != '\0')
1652 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1653 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1657 /* The following will insert a paragraph break after the first character
1658 of the URL candidate, thus breaking the URL. It is expected that the
1659 CFE_LINK attribute should break across both pieces of the URL */
1660 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1661 INSERT_CR;
1662 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1664 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1665 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1666 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1667 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1668 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1669 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1671 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1672 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1673 /* end_offset moved because of paragraph break */
1674 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1675 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1676 if (buffer[end_offset+1] != '\0')
1678 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1679 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1680 if (buffer[end_offset +2] != '\0')
1682 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1683 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1687 /* The following will remove the just-inserted paragraph break, thus
1688 restoring the URL */
1689 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1690 INSERT_BS;
1691 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1693 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1694 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1695 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1696 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1697 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1698 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1700 if (urls[i].is_url)
1702 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1703 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1704 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1705 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1707 else
1709 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1710 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1711 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1712 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1714 if (buffer[end_offset] != '\0')
1716 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1717 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1718 if (buffer[end_offset +1] != '\0')
1720 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1721 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1725 DestroyWindow(hwndRichEdit);
1726 hwndRichEdit = NULL;
1729 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1730 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1731 SETTEXTEX st;
1733 hwndRichEdit = new_richedit(parent);
1735 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1736 be detected:
1737 1) Set entire text, a la WM_SETTEXT
1738 2) Set a selection of the text to the URL
1739 3) Set a portion of the text at a time, which eventually results in
1740 an URL
1741 All of them should give equivalent results
1744 /* Set entire text in one go, like WM_SETTEXT */
1745 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1746 char * at_pos;
1747 int at_offset;
1748 int end_offset;
1750 st.codepage = CP_ACP;
1751 st.flags = ST_DEFAULT;
1753 at_pos = strchr(templates_delim[j], 'X');
1754 at_offset = at_pos - templates_delim[j];
1755 strncpy(buffer, templates_delim[j], at_offset);
1756 buffer[at_offset] = '\0';
1757 strcat(buffer, urls[i].text);
1758 strcat(buffer, templates_delim[j] + at_offset + 1);
1759 end_offset = at_offset + strlen(urls[i].text);
1761 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1762 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1764 /* This assumes no templates start with the URL itself, and that they
1765 have at least two characters before the URL text */
1766 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1767 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1768 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1769 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1770 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1771 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1773 if (urls[i].is_url)
1775 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1776 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1777 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1778 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1780 else
1782 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1783 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1784 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1785 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1787 if (buffer[end_offset] != '\0')
1789 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1790 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1791 if (buffer[end_offset +1] != '\0')
1793 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1794 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1799 /* Set selection with X to the URL */
1800 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1801 char * at_pos;
1802 int at_offset;
1803 int end_offset;
1805 at_pos = strchr(templates_delim[j], 'X');
1806 at_offset = at_pos - templates_delim[j];
1807 end_offset = at_offset + strlen(urls[i].text);
1809 st.codepage = CP_ACP;
1810 st.flags = ST_DEFAULT;
1811 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1812 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1813 st.flags = ST_SELECTION;
1814 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1815 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1816 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1818 /* This assumes no templates start with the URL itself, and that they
1819 have at least two characters before the URL text */
1820 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1821 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1822 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1823 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1824 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1825 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1827 if (urls[i].is_url)
1829 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1830 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1831 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1832 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1834 else
1836 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1837 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1838 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1839 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1841 if (buffer[end_offset] != '\0')
1843 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1844 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1845 if (buffer[end_offset +1] != '\0')
1847 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1848 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1853 /* Set selection with X to the first character of the URL, then the rest */
1854 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1855 char * at_pos;
1856 int at_offset;
1857 int end_offset;
1859 at_pos = strchr(templates_delim[j], 'X');
1860 at_offset = at_pos - templates_delim[j];
1861 end_offset = at_offset + strlen(urls[i].text);
1863 strcpy(buffer, "YY");
1864 buffer[0] = urls[i].text[0];
1866 st.codepage = CP_ACP;
1867 st.flags = ST_DEFAULT;
1868 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1869 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1870 st.flags = ST_SELECTION;
1871 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1872 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1873 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1874 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1875 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1877 /* This assumes no templates start with the URL itself, and that they
1878 have at least two characters before the URL text */
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1881 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1882 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1883 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1884 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1886 if (urls[i].is_url)
1888 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1889 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1890 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1891 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1893 else
1895 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1896 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1897 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1898 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1900 if (buffer[end_offset] != '\0')
1902 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1903 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1904 if (buffer[end_offset +1] != '\0')
1906 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1907 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1912 DestroyWindow(hwndRichEdit);
1913 hwndRichEdit = NULL;
1916 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
1917 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1918 hwndRichEdit = new_richedit(parent);
1920 /* Set selection with X to the URL */
1921 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1922 char * at_pos;
1923 int at_offset;
1924 int end_offset;
1926 at_pos = strchr(templates_delim[j], 'X');
1927 at_offset = at_pos - templates_delim[j];
1928 end_offset = at_offset + strlen(urls[i].text);
1930 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1931 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
1932 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1933 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
1934 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1936 /* This assumes no templates start with the URL itself, and that they
1937 have at least two characters before the URL text */
1938 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1939 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1940 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1941 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1942 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1943 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1945 if (urls[i].is_url)
1947 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1948 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1949 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1950 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1952 else
1954 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1955 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1956 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1957 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1959 if (buffer[end_offset] != '\0')
1961 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1962 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1963 if (buffer[end_offset +1] != '\0')
1965 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1966 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1971 /* Set selection with X to the first character of the URL, then the rest */
1972 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1973 char * at_pos;
1974 int at_offset;
1975 int end_offset;
1977 at_pos = strchr(templates_delim[j], 'X');
1978 at_offset = at_pos - templates_delim[j];
1979 end_offset = at_offset + strlen(urls[i].text);
1981 strcpy(buffer, "YY");
1982 buffer[0] = urls[i].text[0];
1984 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1985 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
1986 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1987 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
1988 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1989 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
1990 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1992 /* This assumes no templates start with the URL itself, and that they
1993 have at least two characters before the URL text */
1994 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1995 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1996 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1997 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1998 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1999 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2001 if (urls[i].is_url)
2003 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2004 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2005 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2006 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2008 else
2010 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2011 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2012 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2013 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2015 if (buffer[end_offset] != '\0')
2017 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2018 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2019 if (buffer[end_offset +1] != '\0')
2021 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2022 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2027 DestroyWindow(hwndRichEdit);
2028 hwndRichEdit = NULL;
2031 DestroyWindow(parent);
2034 static void test_EM_SCROLL(void)
2036 int i, j;
2037 int r; /* return value */
2038 int expr; /* expected return value */
2039 HWND hwndRichEdit = new_richedit(NULL);
2040 int y_before, y_after; /* units of lines of text */
2042 /* test a richedit box containing a single line of text */
2043 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2044 expr = 0x00010000;
2045 for (i = 0; i < 4; i++) {
2046 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2048 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2049 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2050 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2051 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2052 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2053 "(i == %d)\n", y_after, i);
2057 * test a richedit box that will scroll. There are two general
2058 * cases: the case without any long lines and the case with a long
2059 * line.
2061 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2062 if (i == 0)
2063 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2064 else
2065 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2066 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2067 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2068 "LONG LINE \nb\nc\nd\ne");
2069 for (j = 0; j < 12; j++) /* reset scroll position to top */
2070 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2072 /* get first visible line */
2073 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2074 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2076 /* get new current first visible line */
2077 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2079 ok(((r & 0xffffff00) == 0x00010000) &&
2080 ((r & 0x000000ff) != 0x00000000),
2081 "EM_SCROLL page down didn't scroll by a small positive number of "
2082 "lines (r == 0x%08x)\n", r);
2083 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2084 "(line %d scrolled to line %d\n", y_before, y_after);
2086 y_before = y_after;
2088 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2089 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2090 ok(((r & 0xffffff00) == 0x0001ff00),
2091 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2092 "(r == 0x%08x)\n", r);
2093 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2094 "%d scrolled to line %d\n", y_before, y_after);
2096 y_before = y_after;
2098 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2100 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2102 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2103 "(r == 0x%08x)\n", r);
2104 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2105 "1 line (%d scrolled to %d)\n", y_before, y_after);
2107 y_before = y_after;
2109 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2111 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2113 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2114 "(r == 0x%08x)\n", r);
2115 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2116 "line (%d scrolled to %d)\n", y_before, y_after);
2118 y_before = y_after;
2120 r = SendMessage(hwndRichEdit, EM_SCROLL,
2121 SB_LINEUP, 0); /* lineup beyond top */
2123 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2125 ok(r == 0x00010000,
2126 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2127 ok(y_before == y_after,
2128 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2130 y_before = y_after;
2132 r = SendMessage(hwndRichEdit, EM_SCROLL,
2133 SB_PAGEUP, 0);/*page up beyond top */
2135 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2137 ok(r == 0x00010000,
2138 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2139 ok(y_before == y_after,
2140 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2142 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2143 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2144 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2145 r = SendMessage(hwndRichEdit, EM_SCROLL,
2146 SB_PAGEDOWN, 0); /* page down beyond bot */
2147 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2149 ok(r == 0x00010000,
2150 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2151 ok(y_before == y_after,
2152 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2153 y_before, y_after);
2155 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2156 SendMessage(hwndRichEdit, EM_SCROLL,
2157 SB_LINEDOWN, 0); /* line down beyond bot */
2158 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2160 ok(r == 0x00010000,
2161 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2162 ok(y_before == y_after,
2163 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2164 y_before, y_after);
2166 DestroyWindow(hwndRichEdit);
2169 static void test_EM_SETUNDOLIMIT(void)
2171 /* cases we test for:
2172 * default behaviour - limiting at 100 undo's
2173 * undo disabled - setting a limit of 0
2174 * undo limited - undo limit set to some to some number, like 2
2175 * bad input - sending a negative number should default to 100 undo's */
2177 HWND hwndRichEdit = new_richedit(NULL);
2178 CHARRANGE cr;
2179 int i;
2180 int result;
2182 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2183 cr.cpMin = 0;
2184 cr.cpMax = 1;
2185 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2186 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2187 also, multiple pastes don't combine like WM_CHAR would */
2188 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2190 /* first case - check the default */
2191 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2192 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2193 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2194 for (i=0; i<100; i++) /* Undo 100 of them */
2195 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2196 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2197 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
2199 /* second case - cannot undo */
2200 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2201 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
2202 SendMessage(hwndRichEdit,
2203 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
2204 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2205 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
2207 /* third case - set it to an arbitrary number */
2208 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2209 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
2210 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2211 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2212 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2213 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
2214 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
2215 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
2216 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2217 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2218 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
2219 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2220 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2221 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
2223 /* fourth case - setting negative numbers should default to 100 undos */
2224 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2225 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
2226 ok (result == 100,
2227 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
2229 DestroyWindow(hwndRichEdit);
2232 static void test_ES_PASSWORD(void)
2234 /* This isn't hugely testable, so we're just going to run it through its paces */
2236 HWND hwndRichEdit = new_richedit(NULL);
2237 WCHAR result;
2239 /* First, check the default of a regular control */
2240 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2241 ok (result == 0,
2242 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
2244 /* Now, set it to something normal */
2245 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
2246 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2247 ok (result == 120,
2248 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2250 /* Now, set it to something odd */
2251 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
2252 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2253 ok (result == 1234,
2254 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2255 DestroyWindow(hwndRichEdit);
2258 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
2259 LPBYTE pbBuff,
2260 LONG cb,
2261 LONG *pcb)
2263 char** str = (char**)dwCookie;
2264 *pcb = cb;
2265 if (*pcb > 0) {
2266 memcpy(*str, pbBuff, *pcb);
2267 *str += *pcb;
2269 return 0;
2272 static void test_WM_SETTEXT()
2274 HWND hwndRichEdit = new_richedit(NULL);
2275 const char * TestItem1 = "TestSomeText";
2276 const char * TestItem2 = "TestSomeText\r";
2277 const char * TestItem2_after = "TestSomeText\r\n";
2278 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
2279 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
2280 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
2281 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
2282 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
2283 const char * TestItem5_after = "TestSomeText TestSomeText";
2284 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
2285 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
2286 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
2287 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
2289 char buf[1024] = {0};
2290 LRESULT result;
2291 EDITSTREAM es;
2292 char * p;
2294 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
2295 any solitary \r to be converted to \r\n on return. Properly paired
2296 \r\n are not affected. It also shows that the special sequence \r\r\n
2297 gets converted to a single space.
2300 #define TEST_SETTEXT(a, b) \
2301 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
2302 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
2303 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
2304 ok (result == lstrlen(buf), \
2305 "WM_GETTEXT returned %ld instead of expected %u\n", \
2306 result, lstrlen(buf)); \
2307 result = strcmp(b, buf); \
2308 ok(result == 0, \
2309 "WM_SETTEXT round trip: strcmp = %ld\n", result);
2311 TEST_SETTEXT(TestItem1, TestItem1)
2312 TEST_SETTEXT(TestItem2, TestItem2_after)
2313 TEST_SETTEXT(TestItem3, TestItem3_after)
2314 TEST_SETTEXT(TestItem3_after, TestItem3_after)
2315 TEST_SETTEXT(TestItem4, TestItem4_after)
2316 TEST_SETTEXT(TestItem5, TestItem5_after)
2317 TEST_SETTEXT(TestItem6, TestItem6_after)
2318 TEST_SETTEXT(TestItem7, TestItem7_after)
2320 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
2321 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2322 p = buf;
2323 es.dwCookie = (DWORD_PTR)&p;
2324 es.dwError = 0;
2325 es.pfnCallback = test_WM_SETTEXT_esCallback;
2326 memset(buf, 0, sizeof(buf));
2327 SendMessage(hwndRichEdit, EM_STREAMOUT,
2328 (WPARAM)(SF_RTF), (LPARAM)&es);
2329 trace("EM_STREAMOUT produced: \n%s\n", buf);
2330 TEST_SETTEXT(buf, TestItem1)
2332 #undef TEST_SETTEXT
2333 DestroyWindow(hwndRichEdit);
2336 static void test_EM_STREAMOUT(void)
2338 HWND hwndRichEdit = new_richedit(NULL);
2339 int r;
2340 EDITSTREAM es;
2341 char buf[1024] = {0};
2342 char * p;
2344 const char * TestItem1 = "TestSomeText";
2345 const char * TestItem2 = "TestSomeText\r";
2346 const char * TestItem3 = "TestSomeText\r\n";
2348 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2349 p = buf;
2350 es.dwCookie = (DWORD_PTR)&p;
2351 es.dwError = 0;
2352 es.pfnCallback = test_WM_SETTEXT_esCallback;
2353 memset(buf, 0, sizeof(buf));
2354 SendMessage(hwndRichEdit, EM_STREAMOUT,
2355 (WPARAM)(SF_TEXT), (LPARAM)&es);
2356 r = strlen(buf);
2357 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
2358 ok(strcmp(buf, TestItem1) == 0,
2359 "streamed text different, got %s\n", buf);
2361 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
2362 p = buf;
2363 es.dwCookie = (DWORD_PTR)&p;
2364 es.dwError = 0;
2365 es.pfnCallback = test_WM_SETTEXT_esCallback;
2366 memset(buf, 0, sizeof(buf));
2367 SendMessage(hwndRichEdit, EM_STREAMOUT,
2368 (WPARAM)(SF_TEXT), (LPARAM)&es);
2369 r = strlen(buf);
2370 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
2371 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2372 ok(strcmp(buf, TestItem3) == 0,
2373 "streamed text different from, got %s\n", buf);
2374 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
2375 p = buf;
2376 es.dwCookie = (DWORD_PTR)&p;
2377 es.dwError = 0;
2378 es.pfnCallback = test_WM_SETTEXT_esCallback;
2379 memset(buf, 0, sizeof(buf));
2380 SendMessage(hwndRichEdit, EM_STREAMOUT,
2381 (WPARAM)(SF_TEXT), (LPARAM)&es);
2382 r = strlen(buf);
2383 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2384 ok(strcmp(buf, TestItem3) == 0,
2385 "streamed text different, got %s\n", buf);
2387 DestroyWindow(hwndRichEdit);
2390 static void test_EM_SETTEXTEX(void)
2392 HWND hwndRichEdit = new_richedit(NULL);
2393 SETTEXTEX setText;
2394 GETTEXTEX getText;
2395 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2396 'S', 'o', 'm', 'e',
2397 'T', 'e', 'x', 't', 0};
2398 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2399 'S', 'o', 'm', 'e',
2400 'T', 'e', 'x', 't',
2401 '\r', 0};
2402 const char * TestItem2_after = "TestSomeText\r\n";
2403 WCHAR TestItem3[] = {'T', 'e', 's', 't',
2404 'S', 'o', 'm', 'e',
2405 'T', 'e', 'x', 't',
2406 '\r','\n','\r','\n', 0};
2407 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
2408 'S', 'o', 'm', 'e',
2409 'T', 'e', 'x', 't',
2410 '\n','\n', 0};
2411 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
2412 'S', 'o', 'm', 'e',
2413 'T', 'e', 'x', 't',
2414 '\r','\r', 0};
2415 WCHAR TestItem4[] = {'T', 'e', 's', 't',
2416 'S', 'o', 'm', 'e',
2417 'T', 'e', 'x', 't',
2418 '\r','\r','\n','\r',
2419 '\n', 0};
2420 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
2421 'S', 'o', 'm', 'e',
2422 'T', 'e', 'x', 't',
2423 ' ','\r', 0};
2424 #define MAX_BUF_LEN 1024
2425 WCHAR buf[MAX_BUF_LEN];
2426 char * p;
2427 int result;
2428 CHARRANGE cr;
2429 EDITSTREAM es;
2431 setText.codepage = 1200; /* no constant for unicode */
2432 getText.codepage = 1200; /* no constant for unicode */
2433 getText.cb = MAX_BUF_LEN;
2434 getText.flags = GT_DEFAULT;
2435 getText.lpDefaultChar = NULL;
2436 getText.lpUsedDefChar = NULL;
2438 setText.flags = 0;
2439 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2440 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2441 ok(lstrcmpW(buf, TestItem1) == 0,
2442 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2444 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
2445 convert \r to \r\n on return
2447 setText.codepage = 1200; /* no constant for unicode */
2448 getText.codepage = 1200; /* no constant for unicode */
2449 getText.cb = MAX_BUF_LEN;
2450 getText.flags = GT_DEFAULT;
2451 getText.lpDefaultChar = NULL;
2452 getText.lpUsedDefChar = NULL;
2453 setText.flags = 0;
2454 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
2455 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2456 ok(lstrcmpW(buf, TestItem2) == 0,
2457 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2459 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
2460 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
2461 ok(strcmp((const char *)buf, TestItem2_after) == 0,
2462 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
2464 /* Baseline test for just-enough buffer space for string */
2465 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2466 getText.codepage = 1200; /* no constant for unicode */
2467 getText.flags = GT_DEFAULT;
2468 getText.lpDefaultChar = NULL;
2469 getText.lpUsedDefChar = NULL;
2470 memset(buf, 0, MAX_BUF_LEN);
2471 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2472 ok(lstrcmpW(buf, TestItem2) == 0,
2473 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2475 /* When there is enough space for one character, but not both, of the CRLF
2476 pair at the end of the string, the CR is not copied at all. That is,
2477 the caller must not see CRLF pairs truncated to CR at the end of the
2478 string.
2480 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2481 getText.codepage = 1200; /* no constant for unicode */
2482 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
2483 getText.lpDefaultChar = NULL;
2484 getText.lpUsedDefChar = NULL;
2485 memset(buf, 0, MAX_BUF_LEN);
2486 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2487 ok(lstrcmpW(buf, TestItem1) == 0,
2488 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2491 /* \r\n pairs get changed into \r */
2492 setText.codepage = 1200; /* no constant for unicode */
2493 getText.codepage = 1200; /* no constant for unicode */
2494 getText.cb = MAX_BUF_LEN;
2495 getText.flags = GT_DEFAULT;
2496 getText.lpDefaultChar = NULL;
2497 getText.lpUsedDefChar = NULL;
2498 setText.flags = 0;
2499 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
2500 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2501 ok(lstrcmpW(buf, TestItem3_after) == 0,
2502 "EM_SETTEXTEX did not convert properly\n");
2504 /* \n also gets changed to \r */
2505 setText.codepage = 1200; /* no constant for unicode */
2506 getText.codepage = 1200; /* no constant for unicode */
2507 getText.cb = MAX_BUF_LEN;
2508 getText.flags = GT_DEFAULT;
2509 getText.lpDefaultChar = NULL;
2510 getText.lpUsedDefChar = NULL;
2511 setText.flags = 0;
2512 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
2513 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2514 ok(lstrcmpW(buf, TestItem3_after) == 0,
2515 "EM_SETTEXTEX did not convert properly\n");
2517 /* \r\r\n gets changed into single space */
2518 setText.codepage = 1200; /* no constant for unicode */
2519 getText.codepage = 1200; /* no constant for unicode */
2520 getText.cb = MAX_BUF_LEN;
2521 getText.flags = GT_DEFAULT;
2522 getText.lpDefaultChar = NULL;
2523 getText.lpUsedDefChar = NULL;
2524 setText.flags = 0;
2525 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
2526 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2527 ok(lstrcmpW(buf, TestItem4_after) == 0,
2528 "EM_SETTEXTEX did not convert properly\n");
2530 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2531 (WPARAM)&setText, (LPARAM) NULL);
2532 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2534 ok (result == 1,
2535 "EM_SETTEXTEX returned %d, instead of 1\n",result);
2536 ok(lstrlenW(buf) == 0,
2537 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
2539 /* put some text back */
2540 setText.flags = 0;
2541 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2542 /* select some text */
2543 cr.cpMax = 1;
2544 cr.cpMin = 3;
2545 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2546 /* replace current selection */
2547 setText.flags = ST_SELECTION;
2548 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2549 (WPARAM)&setText, (LPARAM) NULL);
2550 ok(result == 0,
2551 "EM_SETTEXTEX with NULL lParam to replace selection"
2552 " with no text should return 0. Got %i\n",
2553 result);
2555 /* put some text back */
2556 setText.flags = 0;
2557 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2558 /* select some text */
2559 cr.cpMax = 1;
2560 cr.cpMin = 3;
2561 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2562 /* replace current selection */
2563 setText.flags = ST_SELECTION;
2564 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2565 (WPARAM)&setText, (LPARAM) TestItem1);
2566 /* get text */
2567 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2568 ok(result == lstrlenW(TestItem1),
2569 "EM_SETTEXTEX with NULL lParam to replace selection"
2570 " with no text should return 0. Got %i\n",
2571 result);
2572 ok(lstrlenW(buf) == 22,
2573 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
2574 lstrlenW(buf) );
2576 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
2577 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
2578 p = (char *)buf;
2579 es.dwCookie = (DWORD_PTR)&p;
2580 es.dwError = 0;
2581 es.pfnCallback = test_WM_SETTEXT_esCallback;
2582 memset(buf, 0, sizeof(buf));
2583 SendMessage(hwndRichEdit, EM_STREAMOUT,
2584 (WPARAM)(SF_RTF), (LPARAM)&es);
2585 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
2587 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
2588 getText.codepage = 1200; /* no constant for unicode */
2589 getText.cb = MAX_BUF_LEN;
2590 getText.flags = GT_DEFAULT;
2591 getText.lpDefaultChar = NULL;
2592 getText.lpUsedDefChar = NULL;
2594 setText.flags = 0;
2595 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
2596 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2597 ok(lstrcmpW(buf, TestItem1) == 0,
2598 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2601 DestroyWindow(hwndRichEdit);
2604 static void test_EM_LIMITTEXT(void)
2606 int ret;
2608 HWND hwndRichEdit = new_richedit(NULL);
2610 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
2611 * about setting the length to -1 for multiline edit controls doesn't happen.
2614 /* Don't check default gettextlimit case. That's done in other tests */
2616 /* Set textlimit to 100 */
2617 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
2618 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2619 ok (ret == 100,
2620 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
2622 /* Set textlimit to 0 */
2623 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
2624 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2625 ok (ret == 65536,
2626 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
2628 /* Set textlimit to -1 */
2629 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
2630 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2631 ok (ret == -1,
2632 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
2634 /* Set textlimit to -2 */
2635 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
2636 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2637 ok (ret == -2,
2638 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
2640 DestroyWindow (hwndRichEdit);
2644 static void test_EM_EXLIMITTEXT(void)
2646 int i, selBegin, selEnd, len1, len2;
2647 int result;
2648 char text[1024 + 1];
2649 char buffer[1024 + 1];
2650 int textlimit = 0; /* multiple of 100 */
2651 HWND hwndRichEdit = new_richedit(NULL);
2653 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2654 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
2656 textlimit = 256000;
2657 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2658 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2659 /* set higher */
2660 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2662 textlimit = 1000;
2663 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2664 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2665 /* set lower */
2666 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2668 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
2669 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2670 /* default for WParam = 0 */
2671 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
2673 textlimit = sizeof(text)-1;
2674 memset(text, 'W', textlimit);
2675 text[sizeof(text)-1] = 0;
2676 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2677 /* maxed out text */
2678 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2680 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2681 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2682 len1 = selEnd - selBegin;
2684 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
2685 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
2686 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
2687 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2688 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2689 len2 = selEnd - selBegin;
2691 ok(len1 != len2,
2692 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2693 len1,len2,i);
2695 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2696 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2697 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
2698 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2699 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2700 len1 = selEnd - selBegin;
2702 ok(len1 != len2,
2703 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2704 len1,len2,i);
2706 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2707 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2708 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
2709 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2710 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2711 len2 = selEnd - selBegin;
2713 ok(len1 == len2,
2714 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2715 len1,len2,i);
2717 /* set text up to the limit, select all the text, then add a char */
2718 textlimit = 5;
2719 memset(text, 'W', textlimit);
2720 text[textlimit] = 0;
2721 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2722 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2723 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2724 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2725 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2726 result = strcmp(buffer, "A");
2727 ok(0 == result, "got string = \"%s\"\n", buffer);
2729 /* WM_SETTEXT not limited */
2730 textlimit = 10;
2731 memset(text, 'W', textlimit);
2732 text[textlimit] = 0;
2733 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
2734 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2735 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2736 i = strlen(buffer);
2737 ok(10 == i, "expected 10 chars\n");
2738 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2739 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2741 /* try inserting more text at end */
2742 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2743 ok(0 == i, "WM_CHAR wasn't processed\n");
2744 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2745 i = strlen(buffer);
2746 ok(10 == i, "expected 10 chars, got %i\n", i);
2747 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2748 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2750 /* try inserting text at beginning */
2751 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
2752 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2753 ok(0 == i, "WM_CHAR wasn't processed\n");
2754 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2755 i = strlen(buffer);
2756 ok(10 == i, "expected 10 chars, got %i\n", i);
2757 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2758 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2760 /* WM_CHAR is limited */
2761 textlimit = 1;
2762 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2763 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2764 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2765 ok(0 == i, "WM_CHAR wasn't processed\n");
2766 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2767 ok(0 == i, "WM_CHAR wasn't processed\n");
2768 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2769 i = strlen(buffer);
2770 ok(1 == i, "expected 1 chars, got %i instead\n", i);
2772 DestroyWindow(hwndRichEdit);
2775 static void test_EM_GETLIMITTEXT(void)
2777 int i;
2778 HWND hwndRichEdit = new_richedit(NULL);
2780 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2781 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
2783 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
2784 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2785 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
2787 DestroyWindow(hwndRichEdit);
2790 static void test_WM_SETFONT(void)
2792 /* There is no invalid input or error conditions for this function.
2793 * NULL wParam and lParam just fall back to their default values
2794 * It should be noted that even if you use a gibberish name for your fonts
2795 * here, it will still work because the name is stored. They will display as
2796 * System, but will report their name to be whatever they were created as */
2798 HWND hwndRichEdit = new_richedit(NULL);
2799 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2800 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2801 FF_DONTCARE, "Marlett");
2802 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2803 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2804 FF_DONTCARE, "MS Sans Serif");
2805 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2806 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2807 FF_DONTCARE, "Courier");
2808 LOGFONTA sentLogFont;
2809 CHARFORMAT2A returnedCF2A;
2811 returnedCF2A.cbSize = sizeof(returnedCF2A);
2813 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2814 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
2815 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2817 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
2818 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2819 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
2820 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2822 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
2823 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2824 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
2825 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2826 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
2827 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2829 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
2830 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2831 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
2832 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2833 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
2834 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2836 /* This last test is special since we send in NULL. We clear the variables
2837 * and just compare to "System" instead of the sent in font name. */
2838 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
2839 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
2840 returnedCF2A.cbSize = sizeof(returnedCF2A);
2842 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
2843 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2844 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
2845 ok (!strcmp("System",returnedCF2A.szFaceName),
2846 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
2848 DestroyWindow(hwndRichEdit);
2852 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
2853 LPBYTE pbBuff,
2854 LONG cb,
2855 LONG *pcb)
2857 const char** str = (const char**)dwCookie;
2858 int size = strlen(*str);
2859 if(size > 3) /* let's make it piecemeal for fun */
2860 size = 3;
2861 *pcb = cb;
2862 if (*pcb > size) {
2863 *pcb = size;
2865 if (*pcb > 0) {
2866 memcpy(pbBuff, *str, *pcb);
2867 *str += *pcb;
2869 return 0;
2872 static void test_EM_GETMODIFY(void)
2874 HWND hwndRichEdit = new_richedit(NULL);
2875 LRESULT result;
2876 SETTEXTEX setText;
2877 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2878 'S', 'o', 'm', 'e',
2879 'T', 'e', 'x', 't', 0};
2880 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2881 'S', 'o', 'm', 'e',
2882 'O', 't', 'h', 'e', 'r',
2883 'T', 'e', 'x', 't', 0};
2884 const char* streamText = "hello world";
2885 CHARFORMAT2 cf2;
2886 PARAFORMAT2 pf2;
2887 EDITSTREAM es;
2889 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2890 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2891 FF_DONTCARE, "Courier");
2893 setText.codepage = 1200; /* no constant for unicode */
2894 setText.flags = ST_KEEPUNDO;
2897 /* modify flag shouldn't be set when richedit is first created */
2898 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2899 ok (result == 0,
2900 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
2902 /* setting modify flag should actually set it */
2903 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
2904 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2905 ok (result != 0,
2906 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
2908 /* clearing modify flag should actually clear it */
2909 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2910 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2911 ok (result == 0,
2912 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
2914 /* setting font doesn't change modify flag */
2915 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2916 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
2917 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2918 ok (result == 0,
2919 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
2921 /* setting text should set modify flag */
2922 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2923 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2924 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2925 ok (result != 0,
2926 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
2928 /* undo previous text doesn't reset modify flag */
2929 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2930 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2931 ok (result != 0,
2932 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
2934 /* set text with no flag to keep undo stack should not set modify flag */
2935 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2936 setText.flags = 0;
2937 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2938 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2939 ok (result == 0,
2940 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
2942 /* WM_SETTEXT doesn't modify */
2943 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2944 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
2945 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2946 ok (result == 0,
2947 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
2949 /* clear the text */
2950 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2951 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
2952 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2953 ok (result == 0,
2954 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
2956 /* replace text */
2957 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2958 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2959 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2960 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
2961 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2962 ok (result != 0,
2963 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
2965 /* copy/paste text 1 */
2966 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2967 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2968 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2969 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2970 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2971 ok (result != 0,
2972 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
2974 /* copy/paste text 2 */
2975 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2976 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2977 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2978 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
2979 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2980 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2981 ok (result != 0,
2982 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
2984 /* press char */
2985 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2986 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
2987 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2988 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2989 ok (result != 0,
2990 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
2992 /* press del */
2993 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2994 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2995 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
2996 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2997 ok (result != 0,
2998 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3000 /* set char format */
3001 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3002 cf2.cbSize = sizeof(CHARFORMAT2);
3003 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3004 (LPARAM) &cf2);
3005 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3006 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3007 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3008 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3009 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3010 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3011 ok (result != 0,
3012 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3014 /* set para format */
3015 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3016 pf2.cbSize = sizeof(PARAFORMAT2);
3017 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3018 (LPARAM) &pf2);
3019 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3020 pf2.wAlignment = PFA_RIGHT;
3021 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3022 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3023 ok (result == 0,
3024 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3026 /* EM_STREAM */
3027 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3028 es.dwCookie = (DWORD_PTR)&streamText;
3029 es.dwError = 0;
3030 es.pfnCallback = test_EM_GETMODIFY_esCallback;
3031 SendMessage(hwndRichEdit, EM_STREAMIN,
3032 (WPARAM)(SF_TEXT), (LPARAM)&es);
3033 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3034 ok (result != 0,
3035 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
3037 DestroyWindow(hwndRichEdit);
3040 struct exsetsel_s {
3041 long min;
3042 long max;
3043 long expected_retval;
3044 int expected_getsel_start;
3045 int expected_getsel_end;
3046 int _exsetsel_todo_wine;
3047 int _getsel_todo_wine;
3050 const struct exsetsel_s exsetsel_tests[] = {
3051 /* sanity tests */
3052 {5, 10, 10, 5, 10, 0, 0},
3053 {15, 17, 17, 15, 17, 0, 0},
3054 /* test cpMax > strlen() */
3055 {0, 100, 18, 0, 18, 0, 1},
3056 /* test cpMin == cpMax */
3057 {5, 5, 5, 5, 5, 0, 0},
3058 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
3059 {-1, 0, 5, 5, 5, 0, 0},
3060 {-1, 17, 5, 5, 5, 0, 0},
3061 {-1, 18, 5, 5, 5, 0, 0},
3062 /* test cpMin < 0 && cpMax < 0 */
3063 {-1, -1, 17, 17, 17, 0, 0},
3064 {-4, -5, 17, 17, 17, 0, 0},
3065 /* test cMin >=0 && cpMax < 0 (bug 6814) */
3066 {0, -1, 18, 0, 18, 0, 1},
3067 {17, -5, 18, 17, 18, 0, 1},
3068 {18, -3, 17, 17, 17, 0, 0},
3069 /* test if cpMin > cpMax */
3070 {15, 19, 18, 15, 18, 0, 1},
3071 {19, 15, 18, 15, 18, 0, 1}
3074 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
3075 CHARRANGE cr;
3076 long result;
3077 int start, end;
3079 cr.cpMin = setsel->min;
3080 cr.cpMax = setsel->max;
3081 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
3083 if (setsel->_exsetsel_todo_wine) {
3084 todo_wine {
3085 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3087 } else {
3088 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3091 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
3093 if (setsel->_getsel_todo_wine) {
3094 todo_wine {
3095 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);
3097 } else {
3098 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);
3102 static void test_EM_EXSETSEL(void)
3104 HWND hwndRichEdit = new_richedit(NULL);
3105 int i;
3106 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
3108 /* sending some text to the window */
3109 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3110 /* 01234567890123456*/
3111 /* 10 */
3113 for (i = 0; i < num_tests; i++) {
3114 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
3117 DestroyWindow(hwndRichEdit);
3120 static void test_EM_REPLACESEL(int redraw)
3122 HWND hwndRichEdit = new_richedit(NULL);
3123 char buffer[1024] = {0};
3124 int r;
3125 GETTEXTEX getText;
3126 CHARRANGE cr;
3128 /* sending some text to the window */
3129 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3130 /* 01234567890123456*/
3131 /* 10 */
3133 /* FIXME add more tests */
3134 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
3135 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
3136 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
3137 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3138 r = strcmp(buffer, "testing");
3139 ok(0 == r, "expected %d, got %d\n", 0, r);
3141 DestroyWindow(hwndRichEdit);
3143 hwndRichEdit = new_richedit(NULL);
3145 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
3146 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
3148 /* Test behavior with carriage returns and newlines */
3149 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3150 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
3151 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
3152 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3153 r = strcmp(buffer, "RichEdit1");
3154 ok(0 == r, "expected %d, got %d\n", 0, r);
3155 getText.cb = 1024;
3156 getText.codepage = CP_ACP;
3157 getText.flags = GT_DEFAULT;
3158 getText.lpDefaultChar = NULL;
3159 getText.lpUsedDefChar = NULL;
3160 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3161 ok(strcmp(buffer, "RichEdit1") == 0,
3162 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
3164 /* Test number of lines reported after EM_REPLACESEL */
3165 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3166 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3168 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3169 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
3170 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
3171 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3172 r = strcmp(buffer, "RichEdit1\r\n");
3173 ok(0 == r, "expected %d, got %d\n", 0, r);
3174 getText.cb = 1024;
3175 getText.codepage = CP_ACP;
3176 getText.flags = GT_DEFAULT;
3177 getText.lpDefaultChar = NULL;
3178 getText.lpUsedDefChar = NULL;
3179 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3180 ok(strcmp(buffer, "RichEdit1\r") == 0,
3181 "EM_GETTEXTEX returned incorrect string\n");
3183 /* Test number of lines reported after EM_REPLACESEL */
3184 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3185 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3187 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
3188 EM_REPLACESEL. The general rule seems to be that Win98's riched20
3189 returns the number of characters *inserted* into the control (after
3190 required conversions), but WinXP's riched20 returns the number of
3191 characters interpreted from the original lParam. Wine's builtin riched20
3192 implements the WinXP behavior.
3194 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3195 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
3196 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
3197 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
3199 /* Test number of lines reported after EM_REPLACESEL */
3200 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3201 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3203 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3204 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3205 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3206 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3208 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3209 r = strcmp(buffer, "RichEdit1\r\n");
3210 ok(0 == r, "expected %d, got %d\n", 0, r);
3211 getText.cb = 1024;
3212 getText.codepage = CP_ACP;
3213 getText.flags = GT_DEFAULT;
3214 getText.lpDefaultChar = NULL;
3215 getText.lpUsedDefChar = NULL;
3216 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3217 ok(strcmp(buffer, "RichEdit1\r") == 0,
3218 "EM_GETTEXTEX returned incorrect string\n");
3220 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3221 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3222 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3223 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3225 /* The following tests show that richedit should handle the special \r\r\n
3226 sequence by turning it into a single space on insertion. However,
3227 EM_REPLACESEL on WinXP returns the number of characters in the original
3228 string.
3231 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3232 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
3233 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
3234 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3235 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3236 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3237 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3239 /* Test the actual string */
3240 getText.cb = 1024;
3241 getText.codepage = CP_ACP;
3242 getText.flags = GT_DEFAULT;
3243 getText.lpDefaultChar = NULL;
3244 getText.lpUsedDefChar = NULL;
3245 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3246 ok(strcmp(buffer, "\r\r") == 0,
3247 "EM_GETTEXTEX returned incorrect string\n");
3249 /* Test number of lines reported after EM_REPLACESEL */
3250 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3251 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3253 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3254 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
3255 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
3256 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
3257 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3258 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3259 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
3260 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
3262 /* Test the actual string */
3263 getText.cb = 1024;
3264 getText.codepage = CP_ACP;
3265 getText.flags = GT_DEFAULT;
3266 getText.lpDefaultChar = NULL;
3267 getText.lpUsedDefChar = NULL;
3268 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3269 ok(strcmp(buffer, " ") == 0,
3270 "EM_GETTEXTEX returned incorrect string\n");
3272 /* Test number of lines reported after EM_REPLACESEL */
3273 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3274 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3276 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3277 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
3278 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3279 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3280 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3281 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3282 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3283 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3285 /* Test the actual string */
3286 getText.cb = 1024;
3287 getText.codepage = CP_ACP;
3288 getText.flags = GT_DEFAULT;
3289 getText.lpDefaultChar = NULL;
3290 getText.lpUsedDefChar = NULL;
3291 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3292 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
3293 "EM_GETTEXTEX returned incorrect string\n");
3295 /* Test number of lines reported after EM_REPLACESEL */
3296 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3297 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3300 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
3301 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
3302 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
3303 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3304 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3305 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3306 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3308 /* Test the actual string */
3309 getText.cb = 1024;
3310 getText.codepage = CP_ACP;
3311 getText.flags = GT_DEFAULT;
3312 getText.lpDefaultChar = NULL;
3313 getText.lpUsedDefChar = NULL;
3314 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3315 ok(strcmp(buffer, " \r") == 0,
3316 "EM_GETTEXTEX returned incorrect string\n");
3318 /* Test number of lines reported after EM_REPLACESEL */
3319 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3320 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3322 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3323 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
3324 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
3325 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
3326 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3327 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3328 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
3329 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
3331 /* Test the actual string */
3332 getText.cb = 1024;
3333 getText.codepage = CP_ACP;
3334 getText.flags = GT_DEFAULT;
3335 getText.lpDefaultChar = NULL;
3336 getText.lpUsedDefChar = NULL;
3337 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3338 ok(strcmp(buffer, " \r\r") == 0,
3339 "EM_GETTEXTEX returned incorrect string\n");
3341 /* Test number of lines reported after EM_REPLACESEL */
3342 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3343 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3345 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3346 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
3347 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
3348 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
3349 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3350 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3351 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
3352 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
3354 /* Test the actual string */
3355 getText.cb = 1024;
3356 getText.codepage = CP_ACP;
3357 getText.flags = GT_DEFAULT;
3358 getText.lpDefaultChar = NULL;
3359 getText.lpUsedDefChar = NULL;
3360 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3361 ok(strcmp(buffer, "\rX\r\r\r") == 0,
3362 "EM_GETTEXTEX returned incorrect string\n");
3364 /* Test number of lines reported after EM_REPLACESEL */
3365 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3366 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
3368 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3369 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
3370 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
3371 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3372 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3373 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3374 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3376 /* Test the actual string */
3377 getText.cb = 1024;
3378 getText.codepage = CP_ACP;
3379 getText.flags = GT_DEFAULT;
3380 getText.lpDefaultChar = NULL;
3381 getText.lpUsedDefChar = NULL;
3382 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3383 ok(strcmp(buffer, "\r\r") == 0,
3384 "EM_GETTEXTEX returned incorrect string\n");
3386 /* Test number of lines reported after EM_REPLACESEL */
3387 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3388 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3390 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3391 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
3392 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3393 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3394 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3395 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3396 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3397 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3399 /* Test the actual string */
3400 getText.cb = 1024;
3401 getText.codepage = CP_ACP;
3402 getText.flags = GT_DEFAULT;
3403 getText.lpDefaultChar = NULL;
3404 getText.lpUsedDefChar = NULL;
3405 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3406 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
3407 "EM_GETTEXTEX returned incorrect string\n");
3409 /* Test number of lines reported after EM_REPLACESEL */
3410 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3411 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3413 DestroyWindow(hwndRichEdit);
3416 static void test_WM_PASTE(void)
3418 MSG msg;
3419 int result;
3420 char buffer[1024] = {0};
3421 char key_info[][3] =
3423 /* VirtualKey, ScanCode, WM_CHAR code */
3424 {'C', 0x2e, 3}, /* Ctrl-C */
3425 {'X', 0x2d, 24}, /* Ctrl-X */
3426 {'V', 0x2f, 22}, /* Ctrl-V */
3427 {'Z', 0x2c, 26}, /* Ctrl-Z */
3428 {'Y', 0x15, 25}, /* Ctrl-Y */
3430 const char* text1 = "testing paste\r";
3431 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
3432 const char* text1_after = "testing paste\r\n";
3433 const char* text2 = "testing paste\r\rtesting paste";
3434 const char* text2_after = "testing paste\r\n\r\ntesting paste";
3435 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
3436 HWND hwndRichEdit = new_richedit(NULL);
3438 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
3439 messages, probably because it inspects the keyboard state itself.
3440 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
3442 #define SEND_CTRL_KEY(hwnd, k) \
3443 keybd_event(VK_CONTROL, 0x1d, 0, 0);\
3444 keybd_event(k[0], k[1], 0, 0);\
3445 keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
3446 keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
3447 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
3448 TranslateMessage(&msg); \
3449 DispatchMessage(&msg); \
3452 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
3453 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
3454 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
3455 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
3456 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
3458 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
3459 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
3461 SEND_CTRL_C(hwndRichEdit) /* Copy */
3462 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3463 SEND_CTRL_V(hwndRichEdit) /* Paste */
3464 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3465 /* Pasted text should be visible at this step */
3466 result = strcmp(text1_step1, buffer);
3467 ok(result == 0,
3468 "test paste: strcmp = %i\n", result);
3469 SEND_CTRL_Z(hwndRichEdit) /* Undo */
3470 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3471 /* Text should be the same as before (except for \r -> \r\n conversion) */
3472 result = strcmp(text1_after, buffer);
3473 ok(result == 0,
3474 "test paste: strcmp = %i\n", result);
3476 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
3477 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
3478 SEND_CTRL_C(hwndRichEdit) /* Copy */
3479 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3480 SEND_CTRL_V(hwndRichEdit) /* Paste */
3481 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3482 /* Pasted text should be visible at this step */
3483 result = strcmp(text3, buffer);
3484 ok(result == 0,
3485 "test paste: strcmp = %i\n", result);
3486 SEND_CTRL_Z(hwndRichEdit) /* Undo */
3487 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3488 /* Text should be the same as before (except for \r -> \r\n conversion) */
3489 result = strcmp(text2_after, buffer);
3490 ok(result == 0,
3491 "test paste: strcmp = %i\n", result);
3492 SEND_CTRL_Y(hwndRichEdit) /* Redo */
3493 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3494 /* Text should revert to post-paste state */
3495 result = strcmp(buffer,text3);
3496 ok(result == 0,
3497 "test paste: strcmp = %i\n", result);
3499 DestroyWindow(hwndRichEdit);
3502 static void test_EM_FORMATRANGE(void)
3504 int r;
3505 FORMATRANGE fr;
3506 HDC hdc;
3507 HWND hwndRichEdit = new_richedit(NULL);
3509 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
3511 hdc = GetDC(hwndRichEdit);
3512 ok(hdc != NULL, "Could not get HDC\n");
3514 fr.hdc = fr.hdcTarget = hdc;
3515 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
3516 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
3517 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
3518 fr.chrg.cpMin = 0;
3519 fr.chrg.cpMax = 20;
3521 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3522 todo_wine {
3523 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3526 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3527 todo_wine {
3528 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
3531 fr.chrg.cpMin = 0;
3532 fr.chrg.cpMax = 10;
3534 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3535 todo_wine {
3536 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
3539 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3540 todo_wine {
3541 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3544 DestroyWindow(hwndRichEdit);
3547 static int nCallbackCount = 0;
3549 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
3550 LONG cb, LONG* pcb)
3552 const char text[] = {'t','e','s','t'};
3554 if (sizeof(text) <= cb)
3556 if ((int)dwCookie != nCallbackCount)
3558 *pcb = 0;
3559 return 0;
3562 memcpy (pbBuff, text, sizeof(text));
3563 *pcb = sizeof(text);
3565 nCallbackCount++;
3567 return 0;
3569 else
3570 return 1; /* indicates callback failed */
3573 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
3574 LPBYTE pbBuff,
3575 LONG cb,
3576 LONG *pcb)
3578 const char** str = (const char**)dwCookie;
3579 int size = strlen(*str);
3580 *pcb = cb;
3581 if (*pcb > size) {
3582 *pcb = size;
3584 if (*pcb > 0) {
3585 memcpy(pbBuff, *str, *pcb);
3586 *str += *pcb;
3588 return 0;
3592 static void test_EM_STREAMIN(void)
3594 HWND hwndRichEdit = new_richedit(NULL);
3595 LRESULT result;
3596 EDITSTREAM es;
3597 char buffer[1024] = {0};
3599 const char * streamText0 = "{\\rtf1 TestSomeText}";
3600 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
3601 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
3603 const char * streamText1 =
3604 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
3605 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
3606 "}\r\n";
3608 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
3609 const char * streamText2 =
3610 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
3611 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
3612 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
3613 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
3614 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
3615 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
3616 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
3618 const char * streamText3 = "RichEdit1";
3620 /* Minimal test without \par at the end */
3621 es.dwCookie = (DWORD_PTR)&streamText0;
3622 es.dwError = 0;
3623 es.pfnCallback = test_EM_STREAMIN_esCallback;
3624 SendMessage(hwndRichEdit, EM_STREAMIN,
3625 (WPARAM)(SF_RTF), (LPARAM)&es);
3627 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3628 ok (result == 12,
3629 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
3630 result = strcmp (buffer,"TestSomeText");
3631 ok (result == 0,
3632 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
3633 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
3635 /* Native richedit 2.0 ignores last \par */
3636 es.dwCookie = (DWORD_PTR)&streamText0a;
3637 es.dwError = 0;
3638 es.pfnCallback = test_EM_STREAMIN_esCallback;
3639 SendMessage(hwndRichEdit, EM_STREAMIN,
3640 (WPARAM)(SF_RTF), (LPARAM)&es);
3642 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3643 ok (result == 12,
3644 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
3645 result = strcmp (buffer,"TestSomeText");
3646 ok (result == 0,
3647 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
3648 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
3650 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
3651 es.dwCookie = (DWORD_PTR)&streamText0b;
3652 es.dwError = 0;
3653 es.pfnCallback = test_EM_STREAMIN_esCallback;
3654 SendMessage(hwndRichEdit, EM_STREAMIN,
3655 (WPARAM)(SF_RTF), (LPARAM)&es);
3657 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3658 ok (result == 14,
3659 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
3660 result = strcmp (buffer,"TestSomeText\r\n");
3661 ok (result == 0,
3662 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
3663 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
3665 es.dwCookie = (DWORD_PTR)&streamText1;
3666 es.dwError = 0;
3667 es.pfnCallback = test_EM_STREAMIN_esCallback;
3668 SendMessage(hwndRichEdit, EM_STREAMIN,
3669 (WPARAM)(SF_RTF), (LPARAM)&es);
3671 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3672 ok (result == 12,
3673 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
3674 result = strcmp (buffer,"TestSomeText");
3675 ok (result == 0,
3676 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3677 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
3679 es.dwCookie = (DWORD_PTR)&streamText2;
3680 es.dwError = 0;
3681 SendMessage(hwndRichEdit, EM_STREAMIN,
3682 (WPARAM)(SF_RTF), (LPARAM)&es);
3684 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3685 ok (result == 0,
3686 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
3687 ok (strlen(buffer) == 0,
3688 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3689 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
3691 es.dwCookie = (DWORD_PTR)&streamText3;
3692 es.dwError = 0;
3693 SendMessage(hwndRichEdit, EM_STREAMIN,
3694 (WPARAM)(SF_RTF), (LPARAM)&es);
3696 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3697 ok (result == 0,
3698 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
3699 ok (strlen(buffer) == 0,
3700 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
3701 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
3703 DestroyWindow(hwndRichEdit);
3706 static void test_EM_StreamIn_Undo(void)
3708 /* The purpose of this test is to determine when a EM_StreamIn should be
3709 * undoable. This is important because WM_PASTE currently uses StreamIn and
3710 * pasting should always be undoable but streaming isn't always.
3712 * cases to test:
3713 * StreamIn plain text without SFF_SELECTION.
3714 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
3715 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
3716 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
3717 * Feel free to add tests for other text modes or StreamIn things.
3721 HWND hwndRichEdit = new_richedit(NULL);
3722 LRESULT result;
3723 EDITSTREAM es;
3724 char buffer[1024] = {0};
3725 const char randomtext[] = "Some text";
3727 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
3729 /* StreamIn, no SFF_SELECTION */
3730 es.dwCookie = nCallbackCount;
3731 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3732 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3733 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3734 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
3735 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3736 result = strcmp (buffer,"test");
3737 ok (result == 0,
3738 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3740 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3741 ok (result == FALSE,
3742 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
3744 /* StreamIn, SFF_SELECTION, but nothing selected */
3745 es.dwCookie = nCallbackCount;
3746 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3747 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3748 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3749 SendMessage(hwndRichEdit, EM_STREAMIN,
3750 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3751 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3752 result = strcmp (buffer,"testSome text");
3753 ok (result == 0,
3754 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3756 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3757 ok (result == TRUE,
3758 "EM_STREAMIN with SFF_SELECTION but no selection set "
3759 "should create an undo\n");
3761 /* StreamIn, SFF_SELECTION, with a selection */
3762 es.dwCookie = nCallbackCount;
3763 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3764 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3765 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
3766 SendMessage(hwndRichEdit, EM_STREAMIN,
3767 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3768 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3769 result = strcmp (buffer,"Sometesttext");
3770 ok (result == 0,
3771 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3773 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3774 ok (result == TRUE,
3775 "EM_STREAMIN with SFF_SELECTION and selection set "
3776 "should create an undo\n");
3780 static BOOL is_em_settextex_supported(HWND hwnd)
3782 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
3783 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
3786 static void test_unicode_conversions(void)
3788 static const WCHAR tW[] = {'t',0};
3789 static const WCHAR teW[] = {'t','e',0};
3790 static const WCHAR textW[] = {'t','e','s','t',0};
3791 static const char textA[] = "test";
3792 char bufA[64];
3793 WCHAR bufW[64];
3794 HWND hwnd;
3795 int is_win9x, em_settextex_supported, ret;
3797 is_win9x = GetVersion() & 0x80000000;
3799 #define set_textA(hwnd, wm_set_text, txt) \
3800 do { \
3801 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
3802 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3803 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3804 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3805 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
3806 } while(0)
3807 #define expect_textA(hwnd, wm_get_text, txt) \
3808 do { \
3809 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3810 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3811 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3812 memset(bufA, 0xAA, sizeof(bufA)); \
3813 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3814 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3815 ret = lstrcmpA(bufA, txt); \
3816 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
3817 } while(0)
3819 #define set_textW(hwnd, wm_set_text, txt) \
3820 do { \
3821 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
3822 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3823 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3824 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3825 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
3826 } while(0)
3827 #define expect_textW(hwnd, wm_get_text, txt) \
3828 do { \
3829 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
3830 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3831 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3832 memset(bufW, 0xAA, sizeof(bufW)); \
3833 if (is_win9x) \
3835 assert(wm_get_text == EM_GETTEXTEX); \
3836 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3837 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3839 else \
3841 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3842 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
3844 ret = lstrcmpW(bufW, txt); \
3845 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
3846 } while(0)
3847 #define expect_empty(hwnd, wm_get_text) \
3848 do { \
3849 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3850 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3851 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3852 memset(bufA, 0xAA, sizeof(bufA)); \
3853 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3854 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
3855 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
3856 } while(0)
3858 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3859 0, 0, 200, 60, 0, 0, 0, 0);
3860 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3862 ret = IsWindowUnicode(hwnd);
3863 if (is_win9x)
3864 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
3865 else
3866 ok(ret, "RichEdit20W should be unicode under NT\n");
3868 /* EM_SETTEXTEX is supported starting from version 3.0 */
3869 em_settextex_supported = is_em_settextex_supported(hwnd);
3870 trace("EM_SETTEXTEX is %ssupported on this platform\n",
3871 em_settextex_supported ? "" : "NOT ");
3873 expect_empty(hwnd, WM_GETTEXT);
3874 expect_empty(hwnd, EM_GETTEXTEX);
3876 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
3877 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3878 expect_textA(hwnd, WM_GETTEXT, "t");
3879 expect_textA(hwnd, EM_GETTEXTEX, "t");
3880 expect_textW(hwnd, EM_GETTEXTEX, tW);
3882 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
3883 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3884 expect_textA(hwnd, WM_GETTEXT, "te");
3885 expect_textA(hwnd, EM_GETTEXTEX, "te");
3886 expect_textW(hwnd, EM_GETTEXTEX, teW);
3888 set_textA(hwnd, WM_SETTEXT, NULL);
3889 expect_empty(hwnd, WM_GETTEXT);
3890 expect_empty(hwnd, EM_GETTEXTEX);
3892 if (is_win9x)
3893 set_textA(hwnd, WM_SETTEXT, textW);
3894 else
3895 set_textA(hwnd, WM_SETTEXT, textA);
3896 expect_textA(hwnd, WM_GETTEXT, textA);
3897 expect_textA(hwnd, EM_GETTEXTEX, textA);
3898 expect_textW(hwnd, EM_GETTEXTEX, textW);
3900 if (em_settextex_supported)
3902 set_textA(hwnd, EM_SETTEXTEX, textA);
3903 expect_textA(hwnd, WM_GETTEXT, textA);
3904 expect_textA(hwnd, EM_GETTEXTEX, textA);
3905 expect_textW(hwnd, EM_GETTEXTEX, textW);
3908 if (!is_win9x)
3910 set_textW(hwnd, WM_SETTEXT, textW);
3911 expect_textW(hwnd, WM_GETTEXT, textW);
3912 expect_textA(hwnd, WM_GETTEXT, textA);
3913 expect_textW(hwnd, EM_GETTEXTEX, textW);
3914 expect_textA(hwnd, EM_GETTEXTEX, textA);
3916 if (em_settextex_supported)
3918 set_textW(hwnd, EM_SETTEXTEX, textW);
3919 expect_textW(hwnd, WM_GETTEXT, textW);
3920 expect_textA(hwnd, WM_GETTEXT, textA);
3921 expect_textW(hwnd, EM_GETTEXTEX, textW);
3922 expect_textA(hwnd, EM_GETTEXTEX, textA);
3925 DestroyWindow(hwnd);
3927 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
3928 0, 0, 200, 60, 0, 0, 0, 0);
3929 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3931 ret = IsWindowUnicode(hwnd);
3932 ok(!ret, "RichEdit20A should NOT be unicode\n");
3934 set_textA(hwnd, WM_SETTEXT, textA);
3935 expect_textA(hwnd, WM_GETTEXT, textA);
3936 expect_textA(hwnd, EM_GETTEXTEX, textA);
3937 expect_textW(hwnd, EM_GETTEXTEX, textW);
3939 if (em_settextex_supported)
3941 set_textA(hwnd, EM_SETTEXTEX, textA);
3942 expect_textA(hwnd, WM_GETTEXT, textA);
3943 expect_textA(hwnd, EM_GETTEXTEX, textA);
3944 expect_textW(hwnd, EM_GETTEXTEX, textW);
3947 if (!is_win9x)
3949 set_textW(hwnd, WM_SETTEXT, textW);
3950 expect_textW(hwnd, WM_GETTEXT, textW);
3951 expect_textA(hwnd, WM_GETTEXT, textA);
3952 expect_textW(hwnd, EM_GETTEXTEX, textW);
3953 expect_textA(hwnd, EM_GETTEXTEX, textA);
3955 if (em_settextex_supported)
3957 set_textW(hwnd, EM_SETTEXTEX, textW);
3958 expect_textW(hwnd, WM_GETTEXT, textW);
3959 expect_textA(hwnd, WM_GETTEXT, textA);
3960 expect_textW(hwnd, EM_GETTEXTEX, textW);
3961 expect_textA(hwnd, EM_GETTEXTEX, textA);
3964 DestroyWindow(hwnd);
3967 static void test_WM_CHAR(void)
3969 HWND hwnd;
3970 int ret;
3971 const char * char_list = "abc\rabc\r";
3972 const char * expected_content_single = "abcabc";
3973 const char * expected_content_multi = "abc\r\nabc\r\n";
3974 char buffer[64] = {0};
3975 const char * p;
3977 /* single-line control must IGNORE carriage returns */
3978 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3979 0, 0, 200, 60, 0, 0, 0, 0);
3980 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3982 p = char_list;
3983 while (*p != '\0') {
3984 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
3985 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
3986 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
3987 SendMessageA(hwnd, WM_KEYUP, *p, 1);
3988 p++;
3991 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3992 ret = strcmp(buffer, expected_content_single);
3993 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3995 DestroyWindow(hwnd);
3997 /* multi-line control inserts CR normally */
3998 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
3999 0, 0, 200, 60, 0, 0, 0, 0);
4000 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4002 p = char_list;
4003 while (*p != '\0') {
4004 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4005 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4006 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4007 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4008 p++;
4011 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4012 ret = strcmp(buffer, expected_content_multi);
4013 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4015 DestroyWindow(hwnd);
4018 static void test_EM_GETTEXTLENGTHEX(void)
4020 HWND hwnd;
4021 GETTEXTLENGTHEX gtl;
4022 int ret;
4023 const char * base_string = "base string";
4024 const char * test_string = "a\nb\n\n\r\n";
4025 const char * test_string_after = "a";
4026 const char * test_string_2 = "a\rtest\rstring";
4027 char buffer[64] = {0};
4029 /* single line */
4030 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4031 0, 0, 200, 60, 0, 0, 0, 0);
4032 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4034 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4035 gtl.codepage = CP_ACP;
4036 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4037 ok(ret == 0, "ret %d\n",ret);
4039 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4040 gtl.codepage = CP_ACP;
4041 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4042 ok(ret == 0, "ret %d\n",ret);
4044 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4046 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4047 gtl.codepage = CP_ACP;
4048 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4049 ok(ret == strlen(base_string), "ret %d\n",ret);
4051 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4052 gtl.codepage = CP_ACP;
4053 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4054 ok(ret == strlen(base_string), "ret %d\n",ret);
4056 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4058 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4059 gtl.codepage = CP_ACP;
4060 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4061 ok(ret == 1, "ret %d\n",ret);
4063 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4064 gtl.codepage = CP_ACP;
4065 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4066 ok(ret == 1, "ret %d\n",ret);
4068 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4069 ret = strcmp(buffer, test_string_after);
4070 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4072 DestroyWindow(hwnd);
4074 /* multi line */
4075 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
4076 0, 0, 200, 60, 0, 0, 0, 0);
4077 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4079 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4080 gtl.codepage = CP_ACP;
4081 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4082 ok(ret == 0, "ret %d\n",ret);
4084 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4085 gtl.codepage = CP_ACP;
4086 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4087 ok(ret == 0, "ret %d\n",ret);
4089 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4091 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4092 gtl.codepage = CP_ACP;
4093 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4094 ok(ret == strlen(base_string), "ret %d\n",ret);
4096 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4097 gtl.codepage = CP_ACP;
4098 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4099 ok(ret == strlen(base_string), "ret %d\n",ret);
4101 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
4103 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4104 gtl.codepage = CP_ACP;
4105 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4106 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
4108 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4109 gtl.codepage = CP_ACP;
4110 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4111 ok(ret == strlen(test_string_2), "ret %d\n",ret);
4113 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4115 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4116 gtl.codepage = CP_ACP;
4117 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4118 ok(ret == 10, "ret %d\n",ret);
4120 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4121 gtl.codepage = CP_ACP;
4122 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4123 ok(ret == 6, "ret %d\n",ret);
4125 DestroyWindow(hwnd);
4129 /* globals that parent and child access when checking event masks & notifications */
4130 static HWND eventMaskEditHwnd = 0;
4131 static int queriedEventMask;
4132 static int watchForEventMask = 0;
4134 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
4135 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4137 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
4139 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4141 return DefWindowProcA(hwnd, message, wParam, lParam);
4144 /* test event masks in combination with WM_COMMAND */
4145 static void test_eventMask(void)
4147 HWND parent;
4148 int ret;
4149 WNDCLASSA cls;
4150 const char text[] = "foo bar\n";
4151 int eventMask;
4153 /* register class to capture WM_COMMAND */
4154 cls.style = 0;
4155 cls.lpfnWndProc = ParentMsgCheckProcA;
4156 cls.cbClsExtra = 0;
4157 cls.cbWndExtra = 0;
4158 cls.hInstance = GetModuleHandleA(0);
4159 cls.hIcon = 0;
4160 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4161 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4162 cls.lpszMenuName = NULL;
4163 cls.lpszClassName = "EventMaskParentClass";
4164 if(!RegisterClassA(&cls)) assert(0);
4166 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4167 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4168 ok (parent != 0, "Failed to create parent window\n");
4170 eventMaskEditHwnd = new_richedit(parent);
4171 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
4173 eventMask = ENM_CHANGE | ENM_UPDATE;
4174 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
4175 ok(ret == ENM_NONE, "wrong event mask\n");
4176 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4177 ok(ret == eventMask, "failed to set event mask\n");
4179 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
4180 queriedEventMask = 0; /* initialize to something other than we expect */
4181 watchForEventMask = EN_CHANGE;
4182 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
4183 ok(ret == TRUE, "failed to set text\n");
4184 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
4185 notification in response to WM_SETTEXT */
4186 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
4187 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
4191 static int received_WM_NOTIFY = 0;
4192 static int modify_at_WM_NOTIFY = 0;
4193 static HWND hwndRichedit_WM_NOTIFY;
4195 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4197 if(message == WM_NOTIFY)
4199 received_WM_NOTIFY = 1;
4200 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
4202 return DefWindowProcA(hwnd, message, wParam, lParam);
4205 static void test_WM_NOTIFY(void)
4207 HWND parent;
4208 WNDCLASSA cls;
4209 CHARFORMAT2 cf2;
4211 /* register class to capture WM_NOTIFY */
4212 cls.style = 0;
4213 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
4214 cls.cbClsExtra = 0;
4215 cls.cbWndExtra = 0;
4216 cls.hInstance = GetModuleHandleA(0);
4217 cls.hIcon = 0;
4218 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4219 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4220 cls.lpszMenuName = NULL;
4221 cls.lpszClassName = "WM_NOTIFY_ParentClass";
4222 if(!RegisterClassA(&cls)) assert(0);
4224 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4225 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4226 ok (parent != 0, "Failed to create parent window\n");
4228 hwndRichedit_WM_NOTIFY = new_richedit(parent);
4229 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
4231 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
4233 /* Notifications for selection change should only be sent when selection
4234 actually changes. EM_SETCHARFORMAT is one message that calls
4235 ME_CommitUndo, which should check whether message should be sent */
4236 received_WM_NOTIFY = 0;
4237 cf2.cbSize = sizeof(CHARFORMAT2);
4238 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4239 (LPARAM) &cf2);
4240 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4241 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4242 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
4243 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4245 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
4246 already at 0. */
4247 received_WM_NOTIFY = 0;
4248 modify_at_WM_NOTIFY = 0;
4249 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4250 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4251 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4253 received_WM_NOTIFY = 0;
4254 modify_at_WM_NOTIFY = 0;
4255 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
4256 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4258 received_WM_NOTIFY = 0;
4259 modify_at_WM_NOTIFY = 0;
4260 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4261 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4262 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4264 DestroyWindow(hwndRichedit_WM_NOTIFY);
4265 DestroyWindow(parent);
4268 START_TEST( editor )
4270 MSG msg;
4271 time_t end;
4273 /* Must explicitly LoadLibrary(). The test has no references to functions in
4274 * RICHED20.DLL, so the linker doesn't actually link to it. */
4275 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
4276 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
4277 test_WM_CHAR();
4278 test_EM_FINDTEXT();
4279 test_EM_GETLINE();
4280 test_EM_POSFROMCHAR();
4281 test_EM_SCROLLCARET();
4282 test_EM_SCROLL();
4283 test_WM_SETTEXT();
4284 test_EM_LINELENGTH();
4285 test_EM_SETCHARFORMAT();
4286 test_EM_SETTEXTMODE();
4287 test_TM_PLAINTEXT();
4288 test_EM_SETOPTIONS();
4289 test_WM_GETTEXT();
4290 test_EM_GETTEXTRANGE();
4291 test_EM_GETSELTEXT();
4292 test_EM_SETUNDOLIMIT();
4293 test_ES_PASSWORD();
4294 test_EM_SETTEXTEX();
4295 test_EM_LIMITTEXT();
4296 test_EM_EXLIMITTEXT();
4297 test_EM_GETLIMITTEXT();
4298 test_WM_SETFONT();
4299 test_EM_GETMODIFY();
4300 test_EM_EXSETSEL();
4301 test_WM_PASTE();
4302 test_EM_STREAMIN();
4303 test_EM_STREAMOUT();
4304 test_EM_StreamIn_Undo();
4305 test_EM_FORMATRANGE();
4306 test_unicode_conversions();
4307 test_EM_GETTEXTLENGTHEX();
4308 test_EM_REPLACESEL(1);
4309 test_EM_REPLACESEL(0);
4310 test_WM_NOTIFY();
4311 test_EM_AUTOURLDETECT();
4312 test_eventMask();
4314 /* Set the environment variable WINETEST_RICHED20 to keep windows
4315 * responsive and open for 30 seconds. This is useful for debugging.
4317 * The message pump uses PeekMessage() to empty the queue and then sleeps for
4318 * 50ms before retrying the queue. */
4319 end = time(NULL) + 30;
4320 if (getenv( "WINETEST_RICHED20" )) {
4321 while (time(NULL) < end) {
4322 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
4323 TranslateMessage(&msg);
4324 DispatchMessage(&msg);
4325 } else {
4326 Sleep(50);
4331 OleFlushClipboard();
4332 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());