push 7f8c39dca3a5819e8ef115eebf7abed537de3a22
[wine/hacks.git] / dlls / riched20 / tests / editor.c
blob064e780a4b884eaddf304f107441df1c6410e587
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);
572 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
573 ok(rc == FALSE, "Should not be able to undo here.\n");
574 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
576 /* A valid flag, CHARFORMAT2 structure minimally filled */
577 memset(&cf2, 0, sizeof(cf2));
578 cf2.cbSize = sizeof(CHARFORMAT2);
579 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
580 (LPARAM) &cf2);
581 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
582 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
583 todo_wine ok(rc == FALSE, "Should not be able to undo here.\n");
584 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
586 /* A valid flag, CHARFORMAT2 structure minimally filled */
587 memset(&cf2, 0, sizeof(cf2));
588 cf2.cbSize = sizeof(CHARFORMAT2);
589 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
590 (LPARAM) &cf2);
591 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
592 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
593 ok(rc == FALSE, "Should not be able to undo here.\n");
594 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
596 /* A valid flag, CHARFORMAT2 structure minimally filled */
597 memset(&cf2, 0, sizeof(cf2));
598 cf2.cbSize = sizeof(CHARFORMAT2);
599 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
600 (LPARAM) &cf2);
601 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
602 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
603 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
604 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
606 /* A valid flag, CHARFORMAT2 structure minimally filled */
607 memset(&cf2, 0, sizeof(cf2));
608 cf2.cbSize = sizeof(CHARFORMAT2);
609 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
610 (LPARAM) &cf2);
611 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
612 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
613 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
614 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
616 cf2.cbSize = sizeof(CHARFORMAT2);
617 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
618 (LPARAM) &cf2);
620 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
621 cf2.cbSize = sizeof(CHARFORMAT2);
622 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
623 (LPARAM) &cf2);
624 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
625 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
627 /* wParam==0 is default char format, does not set modify */
628 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
629 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
630 ok(rc == 0, "Text marked as modified, expected not modified!\n");
631 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
632 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
633 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
634 ok(rc == 0, "Text marked as modified, expected not modified!\n");
636 /* wParam==SCF_SELECTION sets modify if nonempty selection */
637 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
638 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
639 ok(rc == 0, "Text marked as modified, expected not modified!\n");
640 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
641 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
642 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
643 ok(rc == 0, "Text marked as modified, expected not modified!\n");
645 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
646 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
647 ok(rc == 0, "Text marked as modified, expected not modified!\n");
648 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
649 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
650 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
651 ok(rc == 0, "Text marked as modified, expected not modified!\n");
652 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
653 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
654 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
655 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
656 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
658 /* wParam==SCF_ALL sets modify regardless of whether text is present */
659 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
660 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
661 ok(rc == 0, "Text marked as modified, expected not modified!\n");
662 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
663 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
664 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
665 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
667 DestroyWindow(hwndRichEdit);
669 /* EM_GETCHARFORMAT tests */
670 for (i = 0; tested_effects[i]; i++)
672 hwndRichEdit = new_richedit(NULL);
673 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
675 /* Need to set a TrueType font to get consistent CFM_BOLD results */
676 memset(&cf2, 0, sizeof(CHARFORMAT2));
677 cf2.cbSize = sizeof(CHARFORMAT2);
678 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
679 cf2.dwEffects = 0;
680 strcpy(cf2.szFaceName, "Courier New");
681 cf2.wWeight = FW_DONTCARE;
682 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
684 memset(&cf2, 0, sizeof(CHARFORMAT2));
685 cf2.cbSize = sizeof(CHARFORMAT2);
686 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
687 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
688 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
689 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
691 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
692 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
693 ok((cf2.dwEffects & tested_effects[i]) == 0,
694 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
696 memset(&cf2, 0, sizeof(CHARFORMAT2));
697 cf2.cbSize = sizeof(CHARFORMAT2);
698 cf2.dwMask = tested_effects[i];
699 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
700 cf2.dwMask = CFM_SUPERSCRIPT;
701 cf2.dwEffects = tested_effects[i];
702 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
703 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
705 memset(&cf2, 0, sizeof(CHARFORMAT2));
706 cf2.cbSize = sizeof(CHARFORMAT2);
707 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
708 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
709 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
710 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
712 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
713 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
714 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
715 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
717 memset(&cf2, 0, sizeof(CHARFORMAT2));
718 cf2.cbSize = sizeof(CHARFORMAT2);
719 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
720 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
721 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
722 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
724 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
725 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
726 ok((cf2.dwEffects & tested_effects[i]) == 0,
727 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
729 memset(&cf2, 0, sizeof(CHARFORMAT2));
730 cf2.cbSize = sizeof(CHARFORMAT2);
731 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
732 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
733 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
734 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
736 (cf2.dwMask & tested_effects[i]) == 0),
737 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
739 DestroyWindow(hwndRichEdit);
742 for (i = 0; tested_effects[i]; i++)
744 hwndRichEdit = new_richedit(NULL);
745 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
747 /* Need to set a TrueType font to get consistent CFM_BOLD results */
748 memset(&cf2, 0, sizeof(CHARFORMAT2));
749 cf2.cbSize = sizeof(CHARFORMAT2);
750 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
751 cf2.dwEffects = 0;
752 strcpy(cf2.szFaceName, "Courier New");
753 cf2.wWeight = FW_DONTCARE;
754 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
756 memset(&cf2, 0, sizeof(CHARFORMAT2));
757 cf2.cbSize = sizeof(CHARFORMAT2);
758 cf2.dwMask = tested_effects[i];
759 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
760 cf2.dwMask = CFM_SUPERSCRIPT;
761 cf2.dwEffects = tested_effects[i];
762 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
763 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
765 memset(&cf2, 0, sizeof(CHARFORMAT2));
766 cf2.cbSize = sizeof(CHARFORMAT2);
767 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
768 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
769 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
770 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
772 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
773 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
774 ok((cf2.dwEffects & tested_effects[i]) == 0,
775 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
777 memset(&cf2, 0, sizeof(CHARFORMAT2));
778 cf2.cbSize = sizeof(CHARFORMAT2);
779 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
780 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
781 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
782 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
784 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
785 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
786 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
787 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
789 memset(&cf2, 0, sizeof(CHARFORMAT2));
790 cf2.cbSize = sizeof(CHARFORMAT2);
791 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
792 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
793 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
794 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
796 (cf2.dwMask & tested_effects[i]) == 0),
797 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
798 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
799 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
801 DestroyWindow(hwndRichEdit);
804 /* Effects applied on an empty selection should take effect when selection is
805 replaced with text */
806 hwndRichEdit = new_richedit(NULL);
807 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
808 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
810 memset(&cf2, 0, sizeof(CHARFORMAT2));
811 cf2.cbSize = sizeof(CHARFORMAT2);
812 cf2.dwMask = CFM_BOLD;
813 cf2.dwEffects = CFE_BOLD;
814 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
816 /* Selection is now nonempty */
817 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
819 memset(&cf2, 0, sizeof(CHARFORMAT2));
820 cf2.cbSize = sizeof(CHARFORMAT2);
821 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
822 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
824 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
825 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
826 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
827 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
830 /* Set two effects on an empty selection */
831 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
832 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
834 memset(&cf2, 0, sizeof(CHARFORMAT2));
835 cf2.cbSize = sizeof(CHARFORMAT2);
836 cf2.dwMask = CFM_BOLD;
837 cf2.dwEffects = CFE_BOLD;
838 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
839 cf2.dwMask = CFM_ITALIC;
840 cf2.dwEffects = CFE_ITALIC;
841 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
843 /* Selection is now nonempty */
844 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
846 memset(&cf2, 0, sizeof(CHARFORMAT2));
847 cf2.cbSize = sizeof(CHARFORMAT2);
848 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
849 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
851 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
852 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
853 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
854 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
856 /* Setting the (empty) selection to exactly the same place as before should
857 NOT clear the insertion style! */
858 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
859 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
861 memset(&cf2, 0, sizeof(CHARFORMAT2));
862 cf2.cbSize = sizeof(CHARFORMAT2);
863 cf2.dwMask = CFM_BOLD;
864 cf2.dwEffects = CFE_BOLD;
865 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
867 /* Empty selection in same place, insert style should NOT be forgotten here. */
868 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
870 /* Selection is now nonempty */
871 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
873 memset(&cf2, 0, sizeof(CHARFORMAT2));
874 cf2.cbSize = sizeof(CHARFORMAT2);
875 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
876 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
878 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
879 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
880 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
881 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
883 /* Ditto with EM_EXSETSEL */
884 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
885 cr.cpMin = 2; cr.cpMax = 2;
886 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
888 memset(&cf2, 0, sizeof(CHARFORMAT2));
889 cf2.cbSize = sizeof(CHARFORMAT2);
890 cf2.dwMask = CFM_BOLD;
891 cf2.dwEffects = CFE_BOLD;
892 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
894 /* Empty selection in same place, insert style should NOT be forgotten here. */
895 cr.cpMin = 2; cr.cpMax = 2;
896 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
898 /* Selection is now nonempty */
899 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
901 memset(&cf2, 0, sizeof(CHARFORMAT2));
902 cf2.cbSize = sizeof(CHARFORMAT2);
903 cr.cpMin = 2; cr.cpMax = 6;
904 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
905 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
907 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
908 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
909 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
910 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
912 DestroyWindow(hwndRichEdit);
915 static void test_EM_SETTEXTMODE(void)
917 HWND hwndRichEdit = new_richedit(NULL);
918 CHARFORMAT2 cf2, cf2test;
919 CHARRANGE cr;
920 int rc = 0;
922 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
923 /*Insert text into the control*/
925 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
927 /*Attempt to change the control to plain text mode*/
928 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
929 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
931 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
932 If rich text is pasted, it should have the same formatting as the rest
933 of the text in the control*/
935 /*Italicize the text
936 *NOTE: If the default text was already italicized, the test will simply
937 reverse; in other words, it will copy a regular "wine" into a plain
938 text window that uses an italicized format*/
939 cf2.cbSize = sizeof(CHARFORMAT2);
940 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
941 (LPARAM) &cf2);
943 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
944 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
946 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
947 ok(rc == 0, "Text marked as modified, expected not modified!\n");
949 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
950 however, SCF_ALL has been implemented*/
951 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
952 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
954 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
955 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
957 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
959 /*Select the string "wine"*/
960 cr.cpMin = 0;
961 cr.cpMax = 4;
962 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
964 /*Copy the italicized "wine" to the clipboard*/
965 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
967 /*Reset the formatting to default*/
968 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
969 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
970 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
972 /*Clear the text in the control*/
973 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
975 /*Switch to Plain Text Mode*/
976 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
977 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
979 /*Input "wine" again in normal format*/
980 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
982 /*Paste the italicized "wine" into the control*/
983 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
985 /*Select a character from the first "wine" string*/
986 cr.cpMin = 2;
987 cr.cpMax = 3;
988 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
990 /*Retrieve its formatting*/
991 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
992 (LPARAM) &cf2);
994 /*Select a character from the second "wine" string*/
995 cr.cpMin = 5;
996 cr.cpMax = 6;
997 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
999 /*Retrieve its formatting*/
1000 cf2test.cbSize = sizeof(CHARFORMAT2);
1001 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1002 (LPARAM) &cf2test);
1004 /*Compare the two formattings*/
1005 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1006 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1007 cf2.dwEffects, cf2test.dwEffects);
1008 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1009 printing "wine" in the current format(normal)
1010 pasting "wine" from the clipboard(italicized)
1011 comparing the two formats(should differ)*/
1013 /*Attempt to switch with text in control*/
1014 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1015 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1017 /*Clear control*/
1018 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1020 /*Switch into Rich Text mode*/
1021 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1022 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1024 /*Print "wine" in normal formatting into the control*/
1025 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1027 /*Paste italicized "wine" into the control*/
1028 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1030 /*Select text from the first "wine" string*/
1031 cr.cpMin = 1;
1032 cr.cpMax = 3;
1033 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1035 /*Retrieve its formatting*/
1036 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1037 (LPARAM) &cf2);
1039 /*Select text from the second "wine" string*/
1040 cr.cpMin = 6;
1041 cr.cpMax = 7;
1042 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1044 /*Retrieve its formatting*/
1045 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1046 (LPARAM) &cf2test);
1048 /*Test that the two formattings are not the same*/
1049 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1050 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1051 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1053 DestroyWindow(hwndRichEdit);
1056 static void test_TM_PLAINTEXT(void)
1058 /*Tests plain text properties*/
1060 HWND hwndRichEdit = new_richedit(NULL);
1061 CHARFORMAT2 cf2, cf2test;
1062 CHARRANGE cr;
1063 int rc = 0;
1065 /*Switch to plain text mode*/
1067 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1068 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1070 /*Fill control with text*/
1072 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1074 /*Select some text and bold it*/
1076 cr.cpMin = 10;
1077 cr.cpMax = 20;
1078 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1079 cf2.cbSize = sizeof(CHARFORMAT2);
1080 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1081 (LPARAM) &cf2);
1083 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1084 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1086 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1087 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1089 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1090 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1092 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1093 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1095 /*Get the formatting of those characters*/
1097 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1099 /*Get the formatting of some other characters*/
1100 cf2test.cbSize = sizeof(CHARFORMAT2);
1101 cr.cpMin = 21;
1102 cr.cpMax = 30;
1103 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1104 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1106 /*Test that they are the same as plain text allows only one formatting*/
1108 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1109 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1110 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1112 /*Fill the control with a "wine" string, which when inserted will be bold*/
1114 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1116 /*Copy the bolded "wine" string*/
1118 cr.cpMin = 0;
1119 cr.cpMax = 4;
1120 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1121 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1123 /*Swap back to rich text*/
1125 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1126 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1128 /*Set the default formatting to bold italics*/
1130 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1131 cf2.dwMask |= CFM_ITALIC;
1132 cf2.dwEffects ^= CFE_ITALIC;
1133 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1134 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1136 /*Set the text in the control to "wine", which will be bold and italicized*/
1138 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1140 /*Paste the plain text "wine" string, which should take the insert
1141 formatting, which at the moment is bold italics*/
1143 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1145 /*Select the first "wine" string and retrieve its formatting*/
1147 cr.cpMin = 1;
1148 cr.cpMax = 3;
1149 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1150 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1152 /*Select the second "wine" string and retrieve its formatting*/
1154 cr.cpMin = 5;
1155 cr.cpMax = 7;
1156 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1157 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1159 /*Compare the two formattings. They should be the same.*/
1161 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1162 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1163 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1164 DestroyWindow(hwndRichEdit);
1167 static void test_WM_GETTEXT(void)
1169 HWND hwndRichEdit = new_richedit(NULL);
1170 static const char text[] = "Hello. My name is RichEdit!";
1171 static const char text2[] = "Hello. My name is RichEdit!\r";
1172 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1173 char buffer[1024] = {0};
1174 int result;
1176 /* Baseline test with normal-sized buffer */
1177 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1178 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1179 ok(result == lstrlen(buffer),
1180 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1181 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1182 result = strcmp(buffer,text);
1183 ok(result == 0,
1184 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1186 /* Test for returned value of WM_GETTEXTLENGTH */
1187 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1188 ok(result == lstrlen(text),
1189 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1190 result, lstrlen(text));
1192 /* Test for behavior in overflow case */
1193 memset(buffer, 0, 1024);
1194 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1195 ok(result == 0 ||
1196 result == lstrlenA(text) - 1, /* XP, win2k3 */
1197 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1198 result = strcmp(buffer,text);
1199 if (result)
1200 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1201 ok(result == 0,
1202 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1204 /* Baseline test with normal-sized buffer and carriage return */
1205 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1206 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1207 ok(result == lstrlen(buffer),
1208 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1209 result = strcmp(buffer,text2_after);
1210 ok(result == 0,
1211 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1213 /* Test for returned value of WM_GETTEXTLENGTH */
1214 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1215 ok(result == lstrlen(text2_after),
1216 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1217 result, lstrlen(text2_after));
1219 /* Test for behavior of CRLF conversion in case of overflow */
1220 memset(buffer, 0, 1024);
1221 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1222 ok(result == 0 ||
1223 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1224 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1225 result = strcmp(buffer,text2);
1226 if (result)
1227 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1228 ok(result == 0,
1229 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1231 DestroyWindow(hwndRichEdit);
1234 static void test_EM_GETTEXTRANGE(void)
1236 HWND hwndRichEdit = new_richedit(NULL);
1237 const char * text1 = "foo bar\r\nfoo bar";
1238 const char * text2 = "foo bar\rfoo bar";
1239 const char * expect = "bar\rfoo";
1240 char buffer[1024] = {0};
1241 LRESULT result;
1242 TEXTRANGEA textRange;
1244 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1246 textRange.lpstrText = buffer;
1247 textRange.chrg.cpMin = 4;
1248 textRange.chrg.cpMax = 11;
1249 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1250 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1251 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1253 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1255 textRange.lpstrText = buffer;
1256 textRange.chrg.cpMin = 4;
1257 textRange.chrg.cpMax = 11;
1258 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1259 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1260 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1262 DestroyWindow(hwndRichEdit);
1265 static void test_EM_GETSELTEXT(void)
1267 HWND hwndRichEdit = new_richedit(NULL);
1268 const char * text1 = "foo bar\r\nfoo bar";
1269 const char * text2 = "foo bar\rfoo bar";
1270 const char * expect = "bar\rfoo";
1271 char buffer[1024] = {0};
1272 LRESULT result;
1274 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1276 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1277 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1278 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1279 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1281 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1283 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1284 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1285 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1286 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1288 DestroyWindow(hwndRichEdit);
1291 /* FIXME: need to test unimplemented options and robustly test wparam */
1292 static void test_EM_SETOPTIONS(void)
1294 HWND hwndRichEdit = new_richedit(NULL);
1295 static const char text[] = "Hello. My name is RichEdit!";
1296 char buffer[1024] = {0};
1298 /* NEGATIVE TESTING - NO OPTIONS SET */
1299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1300 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1302 /* testing no readonly by sending 'a' to the control*/
1303 SetFocus(hwndRichEdit);
1304 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1305 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1306 ok(buffer[0]=='a',
1307 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1308 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1310 /* READONLY - sending 'a' to the control */
1311 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1312 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1313 SetFocus(hwndRichEdit);
1314 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1315 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1316 ok(buffer[0]==text[0],
1317 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1319 DestroyWindow(hwndRichEdit);
1322 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1324 CHARFORMAT2W text_format;
1325 text_format.cbSize = sizeof(text_format);
1326 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1327 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1328 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1331 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1333 int link_present = 0;
1335 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1336 if (is_url)
1337 { /* control text is url; should get CFE_LINK */
1338 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1340 else
1342 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1346 static HWND new_static_wnd(HWND parent) {
1347 return new_window("Static", 0, parent);
1350 static void test_EM_AUTOURLDETECT(void)
1352 struct urls_s {
1353 const char *text;
1354 int is_url;
1355 } urls[12] = {
1356 {"winehq.org", 0},
1357 {"http://www.winehq.org", 1},
1358 {"http//winehq.org", 0},
1359 {"ww.winehq.org", 0},
1360 {"www.winehq.org", 1},
1361 {"ftp://192.168.1.1", 1},
1362 {"ftp//192.168.1.1", 0},
1363 {"mailto:your@email.com", 1},
1364 {"prospero:prosperoserver", 1},
1365 {"telnet:test", 1},
1366 {"news:newserver", 1},
1367 {"wais:waisserver", 1}
1370 int i, j;
1371 int urlRet=-1;
1372 HWND hwndRichEdit, parent;
1374 /* All of the following should cause the URL to be detected */
1375 const char * templates_delim[] = {
1376 "This is some text with X on it",
1377 "This is some text with (X) on it",
1378 "This is some text with X\r on it",
1379 "This is some text with ---X--- on it",
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",
1385 "This text ends with X",
1387 "This is some text with X) on it",
1388 "This is some text with X--- on it",
1389 "This is some text with X\" on it",
1390 "This is some text with X' on it",
1391 "This is some text with X: on it",
1393 "This is some text with (X on it",
1394 "This is some text with \rX on it",
1395 "This is some text with ---X on it",
1396 "This is some text with \"X on it",
1397 "This is some text with 'X on it",
1398 "This is some text with :X on it",
1400 /* None of these should cause the URL to be detected */
1401 const char * templates_non_delim[] = {
1402 "This is some text with |X| on it",
1403 "This is some text with *X* on it",
1404 "This is some text with /X/ on it",
1405 "This is some text with +X+ on it",
1406 "This is some text with %X% on it",
1407 "This is some text with #X# on it",
1408 "This is some text with @X@ on it",
1409 "This is some text with \\X\\ on it",
1410 "This is some text with |X on it",
1411 "This is some text with *X on it",
1412 "This is some text with /X on it",
1413 "This is some text with +X on it",
1414 "This is some text with %X on it",
1415 "This is some text with #X on it",
1416 "This is some text with @X on it",
1417 "This is some text with \\X on it",
1419 /* All of these cause the URL detection to be extended by one more byte,
1420 thus demonstrating that the tested character is considered as part
1421 of the URL. */
1422 const char * templates_xten_delim[] = {
1423 "This is some text with X| on it",
1424 "This is some text with X* on it",
1425 "This is some text with X/ on it",
1426 "This is some text with X+ on it",
1427 "This is some text with X% on it",
1428 "This is some text with X# on it",
1429 "This is some text with X@ on it",
1430 "This is some text with X\\ on it",
1432 char buffer[1024];
1433 MSG msg;
1435 parent = new_static_wnd(NULL);
1436 hwndRichEdit = new_richedit(parent);
1437 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1438 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1439 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1440 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1441 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1442 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1443 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1444 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1445 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1446 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1447 /* for each url, check the text to see if CFE_LINK effect is present */
1448 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1450 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1451 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1452 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1454 /* Link detection should happen immediately upon WM_SETTEXT */
1455 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1456 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1457 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1459 DestroyWindow(hwndRichEdit);
1461 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1462 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1463 hwndRichEdit = new_richedit(parent);
1465 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1466 char * at_pos;
1467 int at_offset;
1468 int end_offset;
1470 at_pos = strchr(templates_delim[j], 'X');
1471 at_offset = at_pos - templates_delim[j];
1472 strncpy(buffer, templates_delim[j], at_offset);
1473 buffer[at_offset] = '\0';
1474 strcat(buffer, urls[i].text);
1475 strcat(buffer, templates_delim[j] + at_offset + 1);
1476 end_offset = at_offset + strlen(urls[i].text);
1478 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1479 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1481 /* This assumes no templates start with the URL itself, and that they
1482 have at least two characters before the URL text */
1483 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1484 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1485 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1486 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1487 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1488 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1490 if (urls[i].is_url)
1492 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1493 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1494 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1495 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1497 else
1499 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1500 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1501 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1502 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1504 if (buffer[end_offset] != '\0')
1506 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1507 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1508 if (buffer[end_offset +1] != '\0')
1510 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1511 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1516 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1517 char * at_pos;
1518 int at_offset;
1519 int end_offset;
1521 at_pos = strchr(templates_non_delim[j], 'X');
1522 at_offset = at_pos - templates_non_delim[j];
1523 strncpy(buffer, templates_non_delim[j], at_offset);
1524 buffer[at_offset] = '\0';
1525 strcat(buffer, urls[i].text);
1526 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1527 end_offset = at_offset + strlen(urls[i].text);
1529 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1530 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1532 /* This assumes no templates start with the URL itself, and that they
1533 have at least two characters before the URL text */
1534 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1535 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1536 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1537 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1538 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1539 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1541 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1542 "CFE_LINK incorrectly 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 incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1545 if (buffer[end_offset] != '\0')
1547 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1548 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1549 if (buffer[end_offset +1] != '\0')
1551 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1552 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1557 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1558 char * at_pos;
1559 int at_offset;
1560 int end_offset;
1562 at_pos = strchr(templates_xten_delim[j], 'X');
1563 at_offset = at_pos - templates_xten_delim[j];
1564 strncpy(buffer, templates_xten_delim[j], at_offset);
1565 buffer[at_offset] = '\0';
1566 strcat(buffer, urls[i].text);
1567 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1568 end_offset = at_offset + strlen(urls[i].text);
1570 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1571 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1573 /* This assumes no templates start with the URL itself, and that they
1574 have at least two characters before the URL text */
1575 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1576 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1577 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1578 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1579 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1580 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1582 if (urls[i].is_url)
1584 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1585 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1586 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1587 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1588 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1589 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1591 else
1593 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1594 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1595 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1596 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1597 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1598 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1600 if (buffer[end_offset +1] != '\0')
1602 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1603 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1604 if (buffer[end_offset +2] != '\0')
1606 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1607 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1612 DestroyWindow(hwndRichEdit);
1613 hwndRichEdit = NULL;
1616 #define INSERT_CR \
1617 do { \
1618 keybd_event('\r', 0x1c, 0, 0); \
1619 keybd_event('\r', 0x1c, KEYEVENTF_KEYUP, 0); \
1620 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
1621 TranslateMessage(&msg); \
1622 DispatchMessage(&msg); \
1624 } while (0)
1626 #define INSERT_BS \
1627 do { \
1628 keybd_event(0x08, 0x0e, 0, 0); \
1629 keybd_event(0x08, 0x0e, KEYEVENTF_KEYUP, 0); \
1630 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
1631 TranslateMessage(&msg); \
1632 DispatchMessage(&msg); \
1634 } while (0)
1636 /* Test detection of URLs within normal text - WM_CHAR case. */
1637 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1638 hwndRichEdit = new_richedit(parent);
1640 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1641 char * at_pos;
1642 int at_offset;
1643 int end_offset;
1644 int u, v;
1646 at_pos = strchr(templates_delim[j], 'X');
1647 at_offset = at_pos - templates_delim[j];
1648 end_offset = at_offset + strlen(urls[i].text);
1650 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1651 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1652 for (u = 0; templates_delim[j][u]; u++) {
1653 if (templates_delim[j][u] == '\r') {
1654 INSERT_CR;
1655 } else if (templates_delim[j][u] != 'X') {
1656 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1657 } else {
1658 for (v = 0; urls[i].text[v]; v++) {
1659 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1663 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1664 trace("Using template: %s\n", templates_delim[j]);
1666 /* This assumes no templates start with the URL itself, and that they
1667 have at least two characters before the URL text */
1668 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1669 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1670 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1671 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1672 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1673 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1675 if (urls[i].is_url)
1677 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1678 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1679 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1680 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1682 else
1684 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1685 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1686 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1687 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1689 if (buffer[end_offset] != '\0')
1691 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1692 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1693 if (buffer[end_offset +1] != '\0')
1695 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1696 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1700 /* The following will insert a paragraph break after the first character
1701 of the URL candidate, thus breaking the URL. It is expected that the
1702 CFE_LINK attribute should break across both pieces of the URL */
1703 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1704 INSERT_CR;
1705 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1707 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1708 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1709 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1710 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1711 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1712 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1714 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1715 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1716 /* end_offset moved because of paragraph break */
1717 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1718 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1719 if (buffer[end_offset+1] != '\0')
1721 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1722 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1723 if (buffer[end_offset +2] != '\0')
1725 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1726 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1730 /* The following will remove the just-inserted paragraph break, thus
1731 restoring the URL */
1732 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1733 INSERT_BS;
1734 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1736 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1737 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1738 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1739 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1740 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1741 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1743 if (urls[i].is_url)
1745 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1746 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1747 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1748 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1750 else
1752 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1753 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1754 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1755 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1757 if (buffer[end_offset] != '\0')
1759 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1760 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1761 if (buffer[end_offset +1] != '\0')
1763 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1764 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1768 DestroyWindow(hwndRichEdit);
1769 hwndRichEdit = NULL;
1772 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1773 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1774 SETTEXTEX st;
1776 hwndRichEdit = new_richedit(parent);
1778 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1779 be detected:
1780 1) Set entire text, a la WM_SETTEXT
1781 2) Set a selection of the text to the URL
1782 3) Set a portion of the text at a time, which eventually results in
1783 an URL
1784 All of them should give equivalent results
1787 /* Set entire text in one go, like WM_SETTEXT */
1788 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1789 char * at_pos;
1790 int at_offset;
1791 int end_offset;
1793 st.codepage = CP_ACP;
1794 st.flags = ST_DEFAULT;
1796 at_pos = strchr(templates_delim[j], 'X');
1797 at_offset = at_pos - templates_delim[j];
1798 strncpy(buffer, templates_delim[j], at_offset);
1799 buffer[at_offset] = '\0';
1800 strcat(buffer, urls[i].text);
1801 strcat(buffer, templates_delim[j] + at_offset + 1);
1802 end_offset = at_offset + strlen(urls[i].text);
1804 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1805 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1807 /* This assumes no templates start with the URL itself, and that they
1808 have at least two characters before the URL text */
1809 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1810 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1811 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1812 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1813 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1814 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1816 if (urls[i].is_url)
1818 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1819 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1820 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1821 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1823 else
1825 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1826 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1827 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1828 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1830 if (buffer[end_offset] != '\0')
1832 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1833 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1834 if (buffer[end_offset +1] != '\0')
1836 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1837 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1842 /* Set selection with X to the URL */
1843 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1844 char * at_pos;
1845 int at_offset;
1846 int end_offset;
1848 at_pos = strchr(templates_delim[j], 'X');
1849 at_offset = at_pos - templates_delim[j];
1850 end_offset = at_offset + strlen(urls[i].text);
1852 st.codepage = CP_ACP;
1853 st.flags = ST_DEFAULT;
1854 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1855 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1856 st.flags = ST_SELECTION;
1857 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1858 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1859 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1861 /* This assumes no templates start with the URL itself, and that they
1862 have at least two characters before the URL text */
1863 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1864 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1865 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1866 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1867 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1868 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1870 if (urls[i].is_url)
1872 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1873 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1874 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1875 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1877 else
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1881 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1882 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1884 if (buffer[end_offset] != '\0')
1886 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1887 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1888 if (buffer[end_offset +1] != '\0')
1890 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1891 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1896 /* Set selection with X to the first character of the URL, then the rest */
1897 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1898 char * at_pos;
1899 int at_offset;
1900 int end_offset;
1902 at_pos = strchr(templates_delim[j], 'X');
1903 at_offset = at_pos - templates_delim[j];
1904 end_offset = at_offset + strlen(urls[i].text);
1906 strcpy(buffer, "YY");
1907 buffer[0] = urls[i].text[0];
1909 st.codepage = CP_ACP;
1910 st.flags = ST_DEFAULT;
1911 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1912 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1913 st.flags = ST_SELECTION;
1914 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1915 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1916 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1917 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1918 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1920 /* This assumes no templates start with the URL itself, and that they
1921 have at least two characters before the URL text */
1922 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1923 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1924 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1925 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1926 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1927 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1929 if (urls[i].is_url)
1931 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1932 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1933 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1934 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1936 else
1938 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1939 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1940 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1941 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1943 if (buffer[end_offset] != '\0')
1945 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1946 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1947 if (buffer[end_offset +1] != '\0')
1949 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1950 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1955 DestroyWindow(hwndRichEdit);
1956 hwndRichEdit = NULL;
1959 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
1960 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1961 hwndRichEdit = new_richedit(parent);
1963 /* Set selection with X to the URL */
1964 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1965 char * at_pos;
1966 int at_offset;
1967 int end_offset;
1969 at_pos = strchr(templates_delim[j], 'X');
1970 at_offset = at_pos - templates_delim[j];
1971 end_offset = at_offset + strlen(urls[i].text);
1973 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1974 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
1975 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1976 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
1977 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1979 /* This assumes no templates start with the URL itself, and that they
1980 have at least two characters before the URL text */
1981 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1982 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1983 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1984 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1985 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1986 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1988 if (urls[i].is_url)
1990 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1991 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1992 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1993 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1995 else
1997 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1998 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1999 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2000 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2002 if (buffer[end_offset] != '\0')
2004 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2005 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2006 if (buffer[end_offset +1] != '\0')
2008 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2009 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2014 /* Set selection with X to the first character of the URL, then the rest */
2015 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2016 char * at_pos;
2017 int at_offset;
2018 int end_offset;
2020 at_pos = strchr(templates_delim[j], 'X');
2021 at_offset = at_pos - templates_delim[j];
2022 end_offset = at_offset + strlen(urls[i].text);
2024 strcpy(buffer, "YY");
2025 buffer[0] = urls[i].text[0];
2027 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2028 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2029 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2030 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2031 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2032 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2033 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2035 /* This assumes no templates start with the URL itself, and that they
2036 have at least two characters before the URL text */
2037 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2038 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2039 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2040 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2041 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2042 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2044 if (urls[i].is_url)
2046 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2047 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2048 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2049 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2051 else
2053 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2054 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2058 if (buffer[end_offset] != '\0')
2060 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2061 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2062 if (buffer[end_offset +1] != '\0')
2064 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2065 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2070 DestroyWindow(hwndRichEdit);
2071 hwndRichEdit = NULL;
2074 DestroyWindow(parent);
2077 static void test_EM_SCROLL(void)
2079 int i, j;
2080 int r; /* return value */
2081 int expr; /* expected return value */
2082 HWND hwndRichEdit = new_richedit(NULL);
2083 int y_before, y_after; /* units of lines of text */
2085 /* test a richedit box containing a single line of text */
2086 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2087 expr = 0x00010000;
2088 for (i = 0; i < 4; i++) {
2089 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2091 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2092 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2093 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2094 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2095 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2096 "(i == %d)\n", y_after, i);
2100 * test a richedit box that will scroll. There are two general
2101 * cases: the case without any long lines and the case with a long
2102 * line.
2104 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2105 if (i == 0)
2106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2107 else
2108 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2109 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2110 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2111 "LONG LINE \nb\nc\nd\ne");
2112 for (j = 0; j < 12; j++) /* reset scroll position to top */
2113 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2115 /* get first visible line */
2116 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2117 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2119 /* get new current first visible line */
2120 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2122 ok(((r & 0xffffff00) == 0x00010000) &&
2123 ((r & 0x000000ff) != 0x00000000),
2124 "EM_SCROLL page down didn't scroll by a small positive number of "
2125 "lines (r == 0x%08x)\n", r);
2126 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2127 "(line %d scrolled to line %d\n", y_before, y_after);
2129 y_before = y_after;
2131 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2132 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2133 ok(((r & 0xffffff00) == 0x0001ff00),
2134 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2135 "(r == 0x%08x)\n", r);
2136 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2137 "%d scrolled to line %d\n", y_before, y_after);
2139 y_before = y_after;
2141 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2143 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2145 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2146 "(r == 0x%08x)\n", r);
2147 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2148 "1 line (%d scrolled to %d)\n", y_before, y_after);
2150 y_before = y_after;
2152 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2154 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2156 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2157 "(r == 0x%08x)\n", r);
2158 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2159 "line (%d scrolled to %d)\n", y_before, y_after);
2161 y_before = y_after;
2163 r = SendMessage(hwndRichEdit, EM_SCROLL,
2164 SB_LINEUP, 0); /* lineup beyond top */
2166 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2168 ok(r == 0x00010000,
2169 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2170 ok(y_before == y_after,
2171 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2173 y_before = y_after;
2175 r = SendMessage(hwndRichEdit, EM_SCROLL,
2176 SB_PAGEUP, 0);/*page up beyond top */
2178 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2180 ok(r == 0x00010000,
2181 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2182 ok(y_before == y_after,
2183 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2185 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2186 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2187 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2188 r = SendMessage(hwndRichEdit, EM_SCROLL,
2189 SB_PAGEDOWN, 0); /* page down beyond bot */
2190 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2192 ok(r == 0x00010000,
2193 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2194 ok(y_before == y_after,
2195 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2196 y_before, y_after);
2198 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2199 SendMessage(hwndRichEdit, EM_SCROLL,
2200 SB_LINEDOWN, 0); /* line down beyond bot */
2201 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2203 ok(r == 0x00010000,
2204 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2205 ok(y_before == y_after,
2206 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2207 y_before, y_after);
2209 DestroyWindow(hwndRichEdit);
2212 static void test_EM_SETUNDOLIMIT(void)
2214 /* cases we test for:
2215 * default behaviour - limiting at 100 undo's
2216 * undo disabled - setting a limit of 0
2217 * undo limited - undo limit set to some to some number, like 2
2218 * bad input - sending a negative number should default to 100 undo's */
2220 HWND hwndRichEdit = new_richedit(NULL);
2221 CHARRANGE cr;
2222 int i;
2223 int result;
2225 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2226 cr.cpMin = 0;
2227 cr.cpMax = 1;
2228 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2229 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2230 also, multiple pastes don't combine like WM_CHAR would */
2231 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2233 /* first case - check the default */
2234 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2235 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2236 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2237 for (i=0; i<100; i++) /* Undo 100 of them */
2238 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2239 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2240 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
2242 /* second case - cannot undo */
2243 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2244 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
2245 SendMessage(hwndRichEdit,
2246 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
2247 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2248 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
2250 /* third case - set it to an arbitrary number */
2251 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2252 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
2253 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2254 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2255 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2256 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
2257 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
2258 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
2259 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2260 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2261 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
2262 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2263 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2264 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
2266 /* fourth case - setting negative numbers should default to 100 undos */
2267 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2268 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
2269 ok (result == 100,
2270 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
2272 DestroyWindow(hwndRichEdit);
2275 static void test_ES_PASSWORD(void)
2277 /* This isn't hugely testable, so we're just going to run it through its paces */
2279 HWND hwndRichEdit = new_richedit(NULL);
2280 WCHAR result;
2282 /* First, check the default of a regular control */
2283 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2284 ok (result == 0,
2285 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
2287 /* Now, set it to something normal */
2288 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
2289 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2290 ok (result == 120,
2291 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2293 /* Now, set it to something odd */
2294 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
2295 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2296 ok (result == 1234,
2297 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2298 DestroyWindow(hwndRichEdit);
2301 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
2302 LPBYTE pbBuff,
2303 LONG cb,
2304 LONG *pcb)
2306 char** str = (char**)dwCookie;
2307 *pcb = cb;
2308 if (*pcb > 0) {
2309 memcpy(*str, pbBuff, *pcb);
2310 *str += *pcb;
2312 return 0;
2315 static void test_WM_SETTEXT()
2317 HWND hwndRichEdit = new_richedit(NULL);
2318 const char * TestItem1 = "TestSomeText";
2319 const char * TestItem2 = "TestSomeText\r";
2320 const char * TestItem2_after = "TestSomeText\r\n";
2321 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
2322 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
2323 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
2324 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
2325 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
2326 const char * TestItem5_after = "TestSomeText TestSomeText";
2327 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
2328 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
2329 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
2330 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
2332 char buf[1024] = {0};
2333 LRESULT result;
2334 EDITSTREAM es;
2335 char * p;
2337 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
2338 any solitary \r to be converted to \r\n on return. Properly paired
2339 \r\n are not affected. It also shows that the special sequence \r\r\n
2340 gets converted to a single space.
2343 #define TEST_SETTEXT(a, b) \
2344 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
2345 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
2346 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
2347 ok (result == lstrlen(buf), \
2348 "WM_GETTEXT returned %ld instead of expected %u\n", \
2349 result, lstrlen(buf)); \
2350 result = strcmp(b, buf); \
2351 ok(result == 0, \
2352 "WM_SETTEXT round trip: strcmp = %ld\n", result);
2354 TEST_SETTEXT(TestItem1, TestItem1)
2355 TEST_SETTEXT(TestItem2, TestItem2_after)
2356 TEST_SETTEXT(TestItem3, TestItem3_after)
2357 TEST_SETTEXT(TestItem3_after, TestItem3_after)
2358 TEST_SETTEXT(TestItem4, TestItem4_after)
2359 TEST_SETTEXT(TestItem5, TestItem5_after)
2360 TEST_SETTEXT(TestItem6, TestItem6_after)
2361 TEST_SETTEXT(TestItem7, TestItem7_after)
2363 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
2364 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2365 p = buf;
2366 es.dwCookie = (DWORD_PTR)&p;
2367 es.dwError = 0;
2368 es.pfnCallback = test_WM_SETTEXT_esCallback;
2369 memset(buf, 0, sizeof(buf));
2370 SendMessage(hwndRichEdit, EM_STREAMOUT,
2371 (WPARAM)(SF_RTF), (LPARAM)&es);
2372 trace("EM_STREAMOUT produced: \n%s\n", buf);
2373 TEST_SETTEXT(buf, TestItem1)
2375 #undef TEST_SETTEXT
2376 DestroyWindow(hwndRichEdit);
2379 static void test_EM_STREAMOUT(void)
2381 HWND hwndRichEdit = new_richedit(NULL);
2382 int r;
2383 EDITSTREAM es;
2384 char buf[1024] = {0};
2385 char * p;
2387 const char * TestItem1 = "TestSomeText";
2388 const char * TestItem2 = "TestSomeText\r";
2389 const char * TestItem3 = "TestSomeText\r\n";
2391 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2392 p = buf;
2393 es.dwCookie = (DWORD_PTR)&p;
2394 es.dwError = 0;
2395 es.pfnCallback = test_WM_SETTEXT_esCallback;
2396 memset(buf, 0, sizeof(buf));
2397 SendMessage(hwndRichEdit, EM_STREAMOUT,
2398 (WPARAM)(SF_TEXT), (LPARAM)&es);
2399 r = strlen(buf);
2400 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
2401 ok(strcmp(buf, TestItem1) == 0,
2402 "streamed text different, got %s\n", buf);
2404 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
2405 p = buf;
2406 es.dwCookie = (DWORD_PTR)&p;
2407 es.dwError = 0;
2408 es.pfnCallback = test_WM_SETTEXT_esCallback;
2409 memset(buf, 0, sizeof(buf));
2410 SendMessage(hwndRichEdit, EM_STREAMOUT,
2411 (WPARAM)(SF_TEXT), (LPARAM)&es);
2412 r = strlen(buf);
2413 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
2414 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2415 ok(strcmp(buf, TestItem3) == 0,
2416 "streamed text different from, got %s\n", buf);
2417 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
2418 p = buf;
2419 es.dwCookie = (DWORD_PTR)&p;
2420 es.dwError = 0;
2421 es.pfnCallback = test_WM_SETTEXT_esCallback;
2422 memset(buf, 0, sizeof(buf));
2423 SendMessage(hwndRichEdit, EM_STREAMOUT,
2424 (WPARAM)(SF_TEXT), (LPARAM)&es);
2425 r = strlen(buf);
2426 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2427 ok(strcmp(buf, TestItem3) == 0,
2428 "streamed text different, got %s\n", buf);
2430 DestroyWindow(hwndRichEdit);
2433 static void test_EM_SETTEXTEX(void)
2435 HWND hwndRichEdit = new_richedit(NULL);
2436 SETTEXTEX setText;
2437 GETTEXTEX getText;
2438 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2439 'S', 'o', 'm', 'e',
2440 'T', 'e', 'x', 't', 0};
2441 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2442 'S', 'o', 'm', 'e',
2443 'T', 'e', 'x', 't',
2444 '\r', 0};
2445 const char * TestItem2_after = "TestSomeText\r\n";
2446 WCHAR TestItem3[] = {'T', 'e', 's', 't',
2447 'S', 'o', 'm', 'e',
2448 'T', 'e', 'x', 't',
2449 '\r','\n','\r','\n', 0};
2450 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
2451 'S', 'o', 'm', 'e',
2452 'T', 'e', 'x', 't',
2453 '\n','\n', 0};
2454 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
2455 'S', 'o', 'm', 'e',
2456 'T', 'e', 'x', 't',
2457 '\r','\r', 0};
2458 WCHAR TestItem4[] = {'T', 'e', 's', 't',
2459 'S', 'o', 'm', 'e',
2460 'T', 'e', 'x', 't',
2461 '\r','\r','\n','\r',
2462 '\n', 0};
2463 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
2464 'S', 'o', 'm', 'e',
2465 'T', 'e', 'x', 't',
2466 ' ','\r', 0};
2467 #define MAX_BUF_LEN 1024
2468 WCHAR buf[MAX_BUF_LEN];
2469 char * p;
2470 int result;
2471 CHARRANGE cr;
2472 EDITSTREAM es;
2474 setText.codepage = 1200; /* no constant for unicode */
2475 getText.codepage = 1200; /* no constant for unicode */
2476 getText.cb = MAX_BUF_LEN;
2477 getText.flags = GT_DEFAULT;
2478 getText.lpDefaultChar = NULL;
2479 getText.lpUsedDefChar = NULL;
2481 setText.flags = 0;
2482 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2483 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2484 ok(lstrcmpW(buf, TestItem1) == 0,
2485 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2487 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
2488 convert \r to \r\n on return
2490 setText.codepage = 1200; /* no constant for unicode */
2491 getText.codepage = 1200; /* no constant for unicode */
2492 getText.cb = MAX_BUF_LEN;
2493 getText.flags = GT_DEFAULT;
2494 getText.lpDefaultChar = NULL;
2495 getText.lpUsedDefChar = NULL;
2496 setText.flags = 0;
2497 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
2498 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2499 ok(lstrcmpW(buf, TestItem2) == 0,
2500 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2502 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
2503 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
2504 ok(strcmp((const char *)buf, TestItem2_after) == 0,
2505 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
2507 /* Baseline test for just-enough buffer space for string */
2508 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2509 getText.codepage = 1200; /* no constant for unicode */
2510 getText.flags = GT_DEFAULT;
2511 getText.lpDefaultChar = NULL;
2512 getText.lpUsedDefChar = NULL;
2513 memset(buf, 0, MAX_BUF_LEN);
2514 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2515 ok(lstrcmpW(buf, TestItem2) == 0,
2516 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2518 /* When there is enough space for one character, but not both, of the CRLF
2519 pair at the end of the string, the CR is not copied at all. That is,
2520 the caller must not see CRLF pairs truncated to CR at the end of the
2521 string.
2523 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2524 getText.codepage = 1200; /* no constant for unicode */
2525 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
2526 getText.lpDefaultChar = NULL;
2527 getText.lpUsedDefChar = NULL;
2528 memset(buf, 0, MAX_BUF_LEN);
2529 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2530 ok(lstrcmpW(buf, TestItem1) == 0,
2531 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2534 /* \r\n pairs get changed into \r */
2535 setText.codepage = 1200; /* no constant for unicode */
2536 getText.codepage = 1200; /* no constant for unicode */
2537 getText.cb = MAX_BUF_LEN;
2538 getText.flags = GT_DEFAULT;
2539 getText.lpDefaultChar = NULL;
2540 getText.lpUsedDefChar = NULL;
2541 setText.flags = 0;
2542 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
2543 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2544 ok(lstrcmpW(buf, TestItem3_after) == 0,
2545 "EM_SETTEXTEX did not convert properly\n");
2547 /* \n also gets changed to \r */
2548 setText.codepage = 1200; /* no constant for unicode */
2549 getText.codepage = 1200; /* no constant for unicode */
2550 getText.cb = MAX_BUF_LEN;
2551 getText.flags = GT_DEFAULT;
2552 getText.lpDefaultChar = NULL;
2553 getText.lpUsedDefChar = NULL;
2554 setText.flags = 0;
2555 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
2556 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2557 ok(lstrcmpW(buf, TestItem3_after) == 0,
2558 "EM_SETTEXTEX did not convert properly\n");
2560 /* \r\r\n gets changed into single space */
2561 setText.codepage = 1200; /* no constant for unicode */
2562 getText.codepage = 1200; /* no constant for unicode */
2563 getText.cb = MAX_BUF_LEN;
2564 getText.flags = GT_DEFAULT;
2565 getText.lpDefaultChar = NULL;
2566 getText.lpUsedDefChar = NULL;
2567 setText.flags = 0;
2568 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
2569 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2570 ok(lstrcmpW(buf, TestItem4_after) == 0,
2571 "EM_SETTEXTEX did not convert properly\n");
2573 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2574 (WPARAM)&setText, (LPARAM) NULL);
2575 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2577 ok (result == 1,
2578 "EM_SETTEXTEX returned %d, instead of 1\n",result);
2579 ok(lstrlenW(buf) == 0,
2580 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
2582 /* put some text back */
2583 setText.flags = 0;
2584 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2585 /* select some text */
2586 cr.cpMax = 1;
2587 cr.cpMin = 3;
2588 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2589 /* replace current selection */
2590 setText.flags = ST_SELECTION;
2591 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2592 (WPARAM)&setText, (LPARAM) NULL);
2593 ok(result == 0,
2594 "EM_SETTEXTEX with NULL lParam to replace selection"
2595 " with no text should return 0. Got %i\n",
2596 result);
2598 /* put some text back */
2599 setText.flags = 0;
2600 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2601 /* select some text */
2602 cr.cpMax = 1;
2603 cr.cpMin = 3;
2604 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2605 /* replace current selection */
2606 setText.flags = ST_SELECTION;
2607 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2608 (WPARAM)&setText, (LPARAM) TestItem1);
2609 /* get text */
2610 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2611 ok(result == lstrlenW(TestItem1),
2612 "EM_SETTEXTEX with NULL lParam to replace selection"
2613 " with no text should return 0. Got %i\n",
2614 result);
2615 ok(lstrlenW(buf) == 22,
2616 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
2617 lstrlenW(buf) );
2619 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
2620 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
2621 p = (char *)buf;
2622 es.dwCookie = (DWORD_PTR)&p;
2623 es.dwError = 0;
2624 es.pfnCallback = test_WM_SETTEXT_esCallback;
2625 memset(buf, 0, sizeof(buf));
2626 SendMessage(hwndRichEdit, EM_STREAMOUT,
2627 (WPARAM)(SF_RTF), (LPARAM)&es);
2628 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
2630 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
2631 getText.codepage = 1200; /* no constant for unicode */
2632 getText.cb = MAX_BUF_LEN;
2633 getText.flags = GT_DEFAULT;
2634 getText.lpDefaultChar = NULL;
2635 getText.lpUsedDefChar = NULL;
2637 setText.flags = 0;
2638 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
2639 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2640 ok(lstrcmpW(buf, TestItem1) == 0,
2641 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2644 DestroyWindow(hwndRichEdit);
2647 static void test_EM_LIMITTEXT(void)
2649 int ret;
2651 HWND hwndRichEdit = new_richedit(NULL);
2653 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
2654 * about setting the length to -1 for multiline edit controls doesn't happen.
2657 /* Don't check default gettextlimit case. That's done in other tests */
2659 /* Set textlimit to 100 */
2660 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
2661 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2662 ok (ret == 100,
2663 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
2665 /* Set textlimit to 0 */
2666 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
2667 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2668 ok (ret == 65536,
2669 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
2671 /* Set textlimit to -1 */
2672 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
2673 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2674 ok (ret == -1,
2675 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
2677 /* Set textlimit to -2 */
2678 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
2679 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2680 ok (ret == -2,
2681 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
2683 DestroyWindow (hwndRichEdit);
2687 static void test_EM_EXLIMITTEXT(void)
2689 int i, selBegin, selEnd, len1, len2;
2690 int result;
2691 char text[1024 + 1];
2692 char buffer[1024 + 1];
2693 int textlimit = 0; /* multiple of 100 */
2694 HWND hwndRichEdit = new_richedit(NULL);
2696 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2697 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
2699 textlimit = 256000;
2700 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2701 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2702 /* set higher */
2703 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2705 textlimit = 1000;
2706 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2707 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2708 /* set lower */
2709 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2711 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
2712 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2713 /* default for WParam = 0 */
2714 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
2716 textlimit = sizeof(text)-1;
2717 memset(text, 'W', textlimit);
2718 text[sizeof(text)-1] = 0;
2719 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2720 /* maxed out text */
2721 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2723 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2724 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2725 len1 = selEnd - selBegin;
2727 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
2728 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
2729 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
2730 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2731 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2732 len2 = selEnd - selBegin;
2734 ok(len1 != len2,
2735 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2736 len1,len2,i);
2738 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2739 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2740 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
2741 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2742 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2743 len1 = selEnd - selBegin;
2745 ok(len1 != len2,
2746 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2747 len1,len2,i);
2749 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2750 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2751 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
2752 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2753 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2754 len2 = selEnd - selBegin;
2756 ok(len1 == len2,
2757 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2758 len1,len2,i);
2760 /* set text up to the limit, select all the text, then add a char */
2761 textlimit = 5;
2762 memset(text, 'W', textlimit);
2763 text[textlimit] = 0;
2764 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2765 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2766 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2767 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2768 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2769 result = strcmp(buffer, "A");
2770 ok(0 == result, "got string = \"%s\"\n", buffer);
2772 /* WM_SETTEXT not limited */
2773 textlimit = 10;
2774 memset(text, 'W', textlimit);
2775 text[textlimit] = 0;
2776 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
2777 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2778 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2779 i = strlen(buffer);
2780 ok(10 == i, "expected 10 chars\n");
2781 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2782 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2784 /* try inserting more text at end */
2785 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2786 ok(0 == i, "WM_CHAR wasn't processed\n");
2787 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2788 i = strlen(buffer);
2789 ok(10 == i, "expected 10 chars, got %i\n", i);
2790 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2791 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2793 /* try inserting text at beginning */
2794 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
2795 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2796 ok(0 == i, "WM_CHAR wasn't processed\n");
2797 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2798 i = strlen(buffer);
2799 ok(10 == i, "expected 10 chars, got %i\n", i);
2800 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2801 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2803 /* WM_CHAR is limited */
2804 textlimit = 1;
2805 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2806 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2807 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2808 ok(0 == i, "WM_CHAR wasn't processed\n");
2809 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2810 ok(0 == i, "WM_CHAR wasn't processed\n");
2811 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2812 i = strlen(buffer);
2813 ok(1 == i, "expected 1 chars, got %i instead\n", i);
2815 DestroyWindow(hwndRichEdit);
2818 static void test_EM_GETLIMITTEXT(void)
2820 int i;
2821 HWND hwndRichEdit = new_richedit(NULL);
2823 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2824 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
2826 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
2827 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2828 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
2830 DestroyWindow(hwndRichEdit);
2833 static void test_WM_SETFONT(void)
2835 /* There is no invalid input or error conditions for this function.
2836 * NULL wParam and lParam just fall back to their default values
2837 * It should be noted that even if you use a gibberish name for your fonts
2838 * here, it will still work because the name is stored. They will display as
2839 * System, but will report their name to be whatever they were created as */
2841 HWND hwndRichEdit = new_richedit(NULL);
2842 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2843 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2844 FF_DONTCARE, "Marlett");
2845 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2846 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2847 FF_DONTCARE, "MS Sans Serif");
2848 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2849 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2850 FF_DONTCARE, "Courier");
2851 LOGFONTA sentLogFont;
2852 CHARFORMAT2A returnedCF2A;
2854 returnedCF2A.cbSize = sizeof(returnedCF2A);
2856 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2857 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
2858 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2860 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
2861 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2862 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
2863 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2865 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
2866 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2867 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
2868 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2869 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
2870 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2872 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
2873 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2874 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
2875 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2876 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
2877 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2879 /* This last test is special since we send in NULL. We clear the variables
2880 * and just compare to "System" instead of the sent in font name. */
2881 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
2882 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
2883 returnedCF2A.cbSize = sizeof(returnedCF2A);
2885 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
2886 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2887 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
2888 ok (!strcmp("System",returnedCF2A.szFaceName),
2889 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
2891 DestroyWindow(hwndRichEdit);
2895 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
2896 LPBYTE pbBuff,
2897 LONG cb,
2898 LONG *pcb)
2900 const char** str = (const char**)dwCookie;
2901 int size = strlen(*str);
2902 if(size > 3) /* let's make it piecemeal for fun */
2903 size = 3;
2904 *pcb = cb;
2905 if (*pcb > size) {
2906 *pcb = size;
2908 if (*pcb > 0) {
2909 memcpy(pbBuff, *str, *pcb);
2910 *str += *pcb;
2912 return 0;
2915 static void test_EM_GETMODIFY(void)
2917 HWND hwndRichEdit = new_richedit(NULL);
2918 LRESULT result;
2919 SETTEXTEX setText;
2920 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2921 'S', 'o', 'm', 'e',
2922 'T', 'e', 'x', 't', 0};
2923 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2924 'S', 'o', 'm', 'e',
2925 'O', 't', 'h', 'e', 'r',
2926 'T', 'e', 'x', 't', 0};
2927 const char* streamText = "hello world";
2928 CHARFORMAT2 cf2;
2929 PARAFORMAT2 pf2;
2930 EDITSTREAM es;
2932 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2933 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2934 FF_DONTCARE, "Courier");
2936 setText.codepage = 1200; /* no constant for unicode */
2937 setText.flags = ST_KEEPUNDO;
2940 /* modify flag shouldn't be set when richedit is first created */
2941 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2942 ok (result == 0,
2943 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
2945 /* setting modify flag should actually set it */
2946 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
2947 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2948 ok (result != 0,
2949 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
2951 /* clearing modify flag should actually clear it */
2952 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2953 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2954 ok (result == 0,
2955 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
2957 /* setting font doesn't change modify flag */
2958 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2959 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
2960 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2961 ok (result == 0,
2962 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
2964 /* setting text should set modify flag */
2965 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2966 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2967 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2968 ok (result != 0,
2969 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
2971 /* undo previous text doesn't reset modify flag */
2972 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2973 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2974 ok (result != 0,
2975 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
2977 /* set text with no flag to keep undo stack should not set modify flag */
2978 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2979 setText.flags = 0;
2980 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2981 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2982 ok (result == 0,
2983 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
2985 /* WM_SETTEXT doesn't modify */
2986 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2987 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
2988 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2989 ok (result == 0,
2990 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
2992 /* clear the text */
2993 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2994 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
2995 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2996 ok (result == 0,
2997 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
2999 /* replace text */
3000 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3001 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3002 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3003 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3004 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3005 ok (result != 0,
3006 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3008 /* copy/paste text 1 */
3009 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3010 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3011 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3012 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3013 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3014 ok (result != 0,
3015 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3017 /* copy/paste text 2 */
3018 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3019 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3020 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3021 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3022 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3023 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3024 ok (result != 0,
3025 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3027 /* press char */
3028 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3029 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3030 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3031 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3032 ok (result != 0,
3033 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3035 /* press del */
3036 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3037 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3038 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
3039 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3040 ok (result != 0,
3041 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
3043 /* set char format */
3044 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3045 cf2.cbSize = sizeof(CHARFORMAT2);
3046 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
3047 (LPARAM) &cf2);
3048 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
3049 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
3050 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3051 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
3052 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
3053 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3054 ok (result != 0,
3055 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
3057 /* set para format */
3058 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3059 pf2.cbSize = sizeof(PARAFORMAT2);
3060 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
3061 (LPARAM) &pf2);
3062 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
3063 pf2.wAlignment = PFA_RIGHT;
3064 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
3065 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3066 ok (result == 0,
3067 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
3069 /* EM_STREAM */
3070 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3071 es.dwCookie = (DWORD_PTR)&streamText;
3072 es.dwError = 0;
3073 es.pfnCallback = test_EM_GETMODIFY_esCallback;
3074 SendMessage(hwndRichEdit, EM_STREAMIN,
3075 (WPARAM)(SF_TEXT), (LPARAM)&es);
3076 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3077 ok (result != 0,
3078 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
3080 DestroyWindow(hwndRichEdit);
3083 struct exsetsel_s {
3084 long min;
3085 long max;
3086 long expected_retval;
3087 int expected_getsel_start;
3088 int expected_getsel_end;
3089 int _exsetsel_todo_wine;
3090 int _getsel_todo_wine;
3093 const struct exsetsel_s exsetsel_tests[] = {
3094 /* sanity tests */
3095 {5, 10, 10, 5, 10, 0, 0},
3096 {15, 17, 17, 15, 17, 0, 0},
3097 /* test cpMax > strlen() */
3098 {0, 100, 18, 0, 18, 0, 1},
3099 /* test cpMin == cpMax */
3100 {5, 5, 5, 5, 5, 0, 0},
3101 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
3102 {-1, 0, 5, 5, 5, 0, 0},
3103 {-1, 17, 5, 5, 5, 0, 0},
3104 {-1, 18, 5, 5, 5, 0, 0},
3105 /* test cpMin < 0 && cpMax < 0 */
3106 {-1, -1, 17, 17, 17, 0, 0},
3107 {-4, -5, 17, 17, 17, 0, 0},
3108 /* test cMin >=0 && cpMax < 0 (bug 6814) */
3109 {0, -1, 18, 0, 18, 0, 1},
3110 {17, -5, 18, 17, 18, 0, 1},
3111 {18, -3, 17, 17, 17, 0, 0},
3112 /* test if cpMin > cpMax */
3113 {15, 19, 18, 15, 18, 0, 1},
3114 {19, 15, 18, 15, 18, 0, 1}
3117 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
3118 CHARRANGE cr;
3119 long result;
3120 int start, end;
3122 cr.cpMin = setsel->min;
3123 cr.cpMax = setsel->max;
3124 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
3126 if (setsel->_exsetsel_todo_wine) {
3127 todo_wine {
3128 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3130 } else {
3131 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3134 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
3136 if (setsel->_getsel_todo_wine) {
3137 todo_wine {
3138 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);
3140 } else {
3141 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);
3145 static void test_EM_EXSETSEL(void)
3147 HWND hwndRichEdit = new_richedit(NULL);
3148 int i;
3149 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
3151 /* sending some text to the window */
3152 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3153 /* 01234567890123456*/
3154 /* 10 */
3156 for (i = 0; i < num_tests; i++) {
3157 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
3160 DestroyWindow(hwndRichEdit);
3163 static void test_EM_REPLACESEL(int redraw)
3165 HWND hwndRichEdit = new_richedit(NULL);
3166 char buffer[1024] = {0};
3167 int r;
3168 GETTEXTEX getText;
3169 CHARRANGE cr;
3171 /* sending some text to the window */
3172 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3173 /* 01234567890123456*/
3174 /* 10 */
3176 /* FIXME add more tests */
3177 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
3178 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
3179 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
3180 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3181 r = strcmp(buffer, "testing");
3182 ok(0 == r, "expected %d, got %d\n", 0, r);
3184 DestroyWindow(hwndRichEdit);
3186 hwndRichEdit = new_richedit(NULL);
3188 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
3189 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
3191 /* Test behavior with carriage returns and newlines */
3192 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3193 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
3194 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
3195 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3196 r = strcmp(buffer, "RichEdit1");
3197 ok(0 == r, "expected %d, got %d\n", 0, r);
3198 getText.cb = 1024;
3199 getText.codepage = CP_ACP;
3200 getText.flags = GT_DEFAULT;
3201 getText.lpDefaultChar = NULL;
3202 getText.lpUsedDefChar = NULL;
3203 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3204 ok(strcmp(buffer, "RichEdit1") == 0,
3205 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
3207 /* Test number of lines reported after EM_REPLACESEL */
3208 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3209 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3211 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3212 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
3213 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
3214 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3215 r = strcmp(buffer, "RichEdit1\r\n");
3216 ok(0 == r, "expected %d, got %d\n", 0, r);
3217 getText.cb = 1024;
3218 getText.codepage = CP_ACP;
3219 getText.flags = GT_DEFAULT;
3220 getText.lpDefaultChar = NULL;
3221 getText.lpUsedDefChar = NULL;
3222 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3223 ok(strcmp(buffer, "RichEdit1\r") == 0,
3224 "EM_GETTEXTEX returned incorrect string\n");
3226 /* Test number of lines reported after EM_REPLACESEL */
3227 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3228 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3230 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
3231 EM_REPLACESEL. The general rule seems to be that Win98's riched20
3232 returns the number of characters *inserted* into the control (after
3233 required conversions), but WinXP's riched20 returns the number of
3234 characters interpreted from the original lParam. Wine's builtin riched20
3235 implements the WinXP behavior.
3237 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3238 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
3239 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
3240 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
3242 /* Test number of lines reported after EM_REPLACESEL */
3243 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3244 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3246 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3247 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3248 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3249 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3251 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3252 r = strcmp(buffer, "RichEdit1\r\n");
3253 ok(0 == r, "expected %d, got %d\n", 0, r);
3254 getText.cb = 1024;
3255 getText.codepage = CP_ACP;
3256 getText.flags = GT_DEFAULT;
3257 getText.lpDefaultChar = NULL;
3258 getText.lpUsedDefChar = NULL;
3259 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3260 ok(strcmp(buffer, "RichEdit1\r") == 0,
3261 "EM_GETTEXTEX returned incorrect string\n");
3263 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3264 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3265 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3266 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3268 /* The following tests show that richedit should handle the special \r\r\n
3269 sequence by turning it into a single space on insertion. However,
3270 EM_REPLACESEL on WinXP returns the number of characters in the original
3271 string.
3274 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3275 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
3276 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
3277 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3278 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3279 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3280 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3282 /* Test the actual string */
3283 getText.cb = 1024;
3284 getText.codepage = CP_ACP;
3285 getText.flags = GT_DEFAULT;
3286 getText.lpDefaultChar = NULL;
3287 getText.lpUsedDefChar = NULL;
3288 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3289 ok(strcmp(buffer, "\r\r") == 0,
3290 "EM_GETTEXTEX returned incorrect string\n");
3292 /* Test number of lines reported after EM_REPLACESEL */
3293 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3294 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3296 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3297 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
3298 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
3299 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
3300 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3301 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3302 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
3303 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
3305 /* Test the actual string */
3306 getText.cb = 1024;
3307 getText.codepage = CP_ACP;
3308 getText.flags = GT_DEFAULT;
3309 getText.lpDefaultChar = NULL;
3310 getText.lpUsedDefChar = NULL;
3311 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3312 ok(strcmp(buffer, " ") == 0,
3313 "EM_GETTEXTEX returned incorrect string\n");
3315 /* Test number of lines reported after EM_REPLACESEL */
3316 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3317 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3319 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3320 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
3321 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3322 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3323 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3324 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3325 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3326 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3328 /* Test the actual string */
3329 getText.cb = 1024;
3330 getText.codepage = CP_ACP;
3331 getText.flags = GT_DEFAULT;
3332 getText.lpDefaultChar = NULL;
3333 getText.lpUsedDefChar = NULL;
3334 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3335 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
3336 "EM_GETTEXTEX returned incorrect string\n");
3338 /* Test number of lines reported after EM_REPLACESEL */
3339 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3340 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3342 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3343 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
3344 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
3345 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
3346 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3347 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3348 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3349 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3351 /* Test the actual string */
3352 getText.cb = 1024;
3353 getText.codepage = CP_ACP;
3354 getText.flags = GT_DEFAULT;
3355 getText.lpDefaultChar = NULL;
3356 getText.lpUsedDefChar = NULL;
3357 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3358 ok(strcmp(buffer, " \r") == 0,
3359 "EM_GETTEXTEX returned incorrect string\n");
3361 /* Test number of lines reported after EM_REPLACESEL */
3362 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3363 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3365 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3366 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
3367 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
3368 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
3369 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3370 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3371 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
3372 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
3374 /* Test the actual string */
3375 getText.cb = 1024;
3376 getText.codepage = CP_ACP;
3377 getText.flags = GT_DEFAULT;
3378 getText.lpDefaultChar = NULL;
3379 getText.lpUsedDefChar = NULL;
3380 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3381 ok(strcmp(buffer, " \r\r") == 0,
3382 "EM_GETTEXTEX returned incorrect string\n");
3384 /* Test number of lines reported after EM_REPLACESEL */
3385 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3386 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3388 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3389 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
3390 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
3391 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
3392 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3393 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3394 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
3395 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
3397 /* Test the actual string */
3398 getText.cb = 1024;
3399 getText.codepage = CP_ACP;
3400 getText.flags = GT_DEFAULT;
3401 getText.lpDefaultChar = NULL;
3402 getText.lpUsedDefChar = NULL;
3403 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3404 ok(strcmp(buffer, "\rX\r\r\r") == 0,
3405 "EM_GETTEXTEX returned incorrect string\n");
3407 /* Test number of lines reported after EM_REPLACESEL */
3408 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3409 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
3411 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3412 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
3413 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
3414 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3415 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3416 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3417 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3419 /* Test the actual string */
3420 getText.cb = 1024;
3421 getText.codepage = CP_ACP;
3422 getText.flags = GT_DEFAULT;
3423 getText.lpDefaultChar = NULL;
3424 getText.lpUsedDefChar = NULL;
3425 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3426 ok(strcmp(buffer, "\r\r") == 0,
3427 "EM_GETTEXTEX returned incorrect string\n");
3429 /* Test number of lines reported after EM_REPLACESEL */
3430 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3431 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3433 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3434 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
3435 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3436 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3437 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3438 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3439 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3440 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3442 /* Test the actual string */
3443 getText.cb = 1024;
3444 getText.codepage = CP_ACP;
3445 getText.flags = GT_DEFAULT;
3446 getText.lpDefaultChar = NULL;
3447 getText.lpUsedDefChar = NULL;
3448 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3449 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
3450 "EM_GETTEXTEX returned incorrect string\n");
3452 /* Test number of lines reported after EM_REPLACESEL */
3453 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3454 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3456 DestroyWindow(hwndRichEdit);
3459 static void test_WM_PASTE(void)
3461 MSG msg;
3462 int result;
3463 char buffer[1024] = {0};
3464 char key_info[][3] =
3466 /* VirtualKey, ScanCode, WM_CHAR code */
3467 {'C', 0x2e, 3}, /* Ctrl-C */
3468 {'X', 0x2d, 24}, /* Ctrl-X */
3469 {'V', 0x2f, 22}, /* Ctrl-V */
3470 {'Z', 0x2c, 26}, /* Ctrl-Z */
3471 {'Y', 0x15, 25}, /* Ctrl-Y */
3473 const char* text1 = "testing paste\r";
3474 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
3475 const char* text1_after = "testing paste\r\n";
3476 const char* text2 = "testing paste\r\rtesting paste";
3477 const char* text2_after = "testing paste\r\n\r\ntesting paste";
3478 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
3479 HWND hwndRichEdit = new_richedit(NULL);
3481 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
3482 messages, probably because it inspects the keyboard state itself.
3483 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
3485 #define SEND_CTRL_KEY(hwnd, k) \
3486 keybd_event(VK_CONTROL, 0x1d, 0, 0);\
3487 keybd_event(k[0], k[1], 0, 0);\
3488 keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
3489 keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
3490 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
3491 TranslateMessage(&msg); \
3492 DispatchMessage(&msg); \
3495 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
3496 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
3497 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
3498 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
3499 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
3501 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
3502 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
3504 SEND_CTRL_C(hwndRichEdit) /* Copy */
3505 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3506 SEND_CTRL_V(hwndRichEdit) /* Paste */
3507 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3508 /* Pasted text should be visible at this step */
3509 result = strcmp(text1_step1, buffer);
3510 ok(result == 0,
3511 "test paste: strcmp = %i\n", result);
3512 SEND_CTRL_Z(hwndRichEdit) /* Undo */
3513 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3514 /* Text should be the same as before (except for \r -> \r\n conversion) */
3515 result = strcmp(text1_after, buffer);
3516 ok(result == 0,
3517 "test paste: strcmp = %i\n", result);
3519 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
3520 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
3521 SEND_CTRL_C(hwndRichEdit) /* Copy */
3522 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3523 SEND_CTRL_V(hwndRichEdit) /* Paste */
3524 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3525 /* Pasted text should be visible at this step */
3526 result = strcmp(text3, buffer);
3527 ok(result == 0,
3528 "test paste: strcmp = %i\n", result);
3529 SEND_CTRL_Z(hwndRichEdit) /* Undo */
3530 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3531 /* Text should be the same as before (except for \r -> \r\n conversion) */
3532 result = strcmp(text2_after, buffer);
3533 ok(result == 0,
3534 "test paste: strcmp = %i\n", result);
3535 SEND_CTRL_Y(hwndRichEdit) /* Redo */
3536 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3537 /* Text should revert to post-paste state */
3538 result = strcmp(buffer,text3);
3539 ok(result == 0,
3540 "test paste: strcmp = %i\n", result);
3542 DestroyWindow(hwndRichEdit);
3545 static void test_EM_FORMATRANGE(void)
3547 int r;
3548 FORMATRANGE fr;
3549 HDC hdc;
3550 HWND hwndRichEdit = new_richedit(NULL);
3552 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
3554 hdc = GetDC(hwndRichEdit);
3555 ok(hdc != NULL, "Could not get HDC\n");
3557 fr.hdc = fr.hdcTarget = hdc;
3558 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
3559 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
3560 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
3561 fr.chrg.cpMin = 0;
3562 fr.chrg.cpMax = 20;
3564 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3565 todo_wine {
3566 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3569 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3570 todo_wine {
3571 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
3574 fr.chrg.cpMin = 0;
3575 fr.chrg.cpMax = 10;
3577 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3578 todo_wine {
3579 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
3582 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3583 todo_wine {
3584 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3587 DestroyWindow(hwndRichEdit);
3590 static int nCallbackCount = 0;
3592 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
3593 LONG cb, LONG* pcb)
3595 const char text[] = {'t','e','s','t'};
3597 if (sizeof(text) <= cb)
3599 if ((int)dwCookie != nCallbackCount)
3601 *pcb = 0;
3602 return 0;
3605 memcpy (pbBuff, text, sizeof(text));
3606 *pcb = sizeof(text);
3608 nCallbackCount++;
3610 return 0;
3612 else
3613 return 1; /* indicates callback failed */
3616 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
3617 LPBYTE pbBuff,
3618 LONG cb,
3619 LONG *pcb)
3621 const char** str = (const char**)dwCookie;
3622 int size = strlen(*str);
3623 *pcb = cb;
3624 if (*pcb > size) {
3625 *pcb = size;
3627 if (*pcb > 0) {
3628 memcpy(pbBuff, *str, *pcb);
3629 *str += *pcb;
3631 return 0;
3634 struct StringWithLength {
3635 int length;
3636 char *buffer;
3639 /* This callback is used to handled the null characters in a string. */
3640 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
3641 LPBYTE pbBuff,
3642 LONG cb,
3643 LONG *pcb)
3645 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
3646 int size = str->length;
3647 *pcb = cb;
3648 if (*pcb > size) {
3649 *pcb = size;
3651 if (*pcb > 0) {
3652 memcpy(pbBuff, str->buffer, *pcb);
3653 str->buffer += *pcb;
3654 str->length -= *pcb;
3656 return 0;
3659 static void test_EM_STREAMIN(void)
3661 HWND hwndRichEdit = new_richedit(NULL);
3662 LRESULT result;
3663 EDITSTREAM es;
3664 char buffer[1024] = {0};
3666 const char * streamText0 = "{\\rtf1 TestSomeText}";
3667 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
3668 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
3670 const char * streamText1 =
3671 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
3672 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
3673 "}\r\n";
3675 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
3676 const char * streamText2 =
3677 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
3678 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
3679 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
3680 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
3681 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
3682 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
3683 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
3685 const char * streamText3 = "RichEdit1";
3687 struct StringWithLength cookieForStream4;
3688 const char * streamText4 =
3689 "This text just needs to be long enough to cause run to be split onto "\
3690 "two seperate lines and make sure the null terminating character is "\
3691 "handled properly.\0";
3692 int length4 = strlen(streamText4) + 1;
3693 cookieForStream4.buffer = (char *)streamText4;
3694 cookieForStream4.length = length4;
3696 /* Minimal test without \par at the end */
3697 es.dwCookie = (DWORD_PTR)&streamText0;
3698 es.dwError = 0;
3699 es.pfnCallback = test_EM_STREAMIN_esCallback;
3700 SendMessage(hwndRichEdit, EM_STREAMIN,
3701 (WPARAM)(SF_RTF), (LPARAM)&es);
3703 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3704 ok (result == 12,
3705 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
3706 result = strcmp (buffer,"TestSomeText");
3707 ok (result == 0,
3708 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
3709 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
3711 /* Native richedit 2.0 ignores last \par */
3712 es.dwCookie = (DWORD_PTR)&streamText0a;
3713 es.dwError = 0;
3714 es.pfnCallback = test_EM_STREAMIN_esCallback;
3715 SendMessage(hwndRichEdit, EM_STREAMIN,
3716 (WPARAM)(SF_RTF), (LPARAM)&es);
3718 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3719 ok (result == 12,
3720 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
3721 result = strcmp (buffer,"TestSomeText");
3722 ok (result == 0,
3723 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
3724 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
3726 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
3727 es.dwCookie = (DWORD_PTR)&streamText0b;
3728 es.dwError = 0;
3729 es.pfnCallback = test_EM_STREAMIN_esCallback;
3730 SendMessage(hwndRichEdit, EM_STREAMIN,
3731 (WPARAM)(SF_RTF), (LPARAM)&es);
3733 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3734 ok (result == 14,
3735 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
3736 result = strcmp (buffer,"TestSomeText\r\n");
3737 ok (result == 0,
3738 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
3739 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
3741 es.dwCookie = (DWORD_PTR)&streamText1;
3742 es.dwError = 0;
3743 es.pfnCallback = test_EM_STREAMIN_esCallback;
3744 SendMessage(hwndRichEdit, EM_STREAMIN,
3745 (WPARAM)(SF_RTF), (LPARAM)&es);
3747 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3748 ok (result == 12,
3749 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
3750 result = strcmp (buffer,"TestSomeText");
3751 ok (result == 0,
3752 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3753 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
3755 es.dwCookie = (DWORD_PTR)&streamText2;
3756 es.dwError = 0;
3757 SendMessage(hwndRichEdit, EM_STREAMIN,
3758 (WPARAM)(SF_RTF), (LPARAM)&es);
3760 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3761 ok (result == 0,
3762 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
3763 ok (strlen(buffer) == 0,
3764 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3765 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
3767 es.dwCookie = (DWORD_PTR)&streamText3;
3768 es.dwError = 0;
3769 SendMessage(hwndRichEdit, EM_STREAMIN,
3770 (WPARAM)(SF_RTF), (LPARAM)&es);
3772 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3773 ok (result == 0,
3774 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
3775 ok (strlen(buffer) == 0,
3776 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
3777 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
3779 es.dwCookie = (DWORD_PTR)&cookieForStream4;
3780 es.dwError = 0;
3781 es.pfnCallback = test_EM_STREAMIN_esCallback2;
3782 SendMessage(hwndRichEdit, EM_STREAMIN,
3783 (WPARAM)(SF_TEXT), (LPARAM)&es);
3785 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3786 ok (result == length4,
3787 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
3788 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
3790 DestroyWindow(hwndRichEdit);
3793 static void test_EM_StreamIn_Undo(void)
3795 /* The purpose of this test is to determine when a EM_StreamIn should be
3796 * undoable. This is important because WM_PASTE currently uses StreamIn and
3797 * pasting should always be undoable but streaming isn't always.
3799 * cases to test:
3800 * StreamIn plain text without SFF_SELECTION.
3801 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
3802 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
3803 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
3804 * Feel free to add tests for other text modes or StreamIn things.
3808 HWND hwndRichEdit = new_richedit(NULL);
3809 LRESULT result;
3810 EDITSTREAM es;
3811 char buffer[1024] = {0};
3812 const char randomtext[] = "Some text";
3814 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
3816 /* StreamIn, no SFF_SELECTION */
3817 es.dwCookie = nCallbackCount;
3818 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3819 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3820 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3821 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
3822 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3823 result = strcmp (buffer,"test");
3824 ok (result == 0,
3825 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3827 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3828 ok (result == FALSE,
3829 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
3831 /* StreamIn, SFF_SELECTION, but nothing selected */
3832 es.dwCookie = nCallbackCount;
3833 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3834 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3835 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3836 SendMessage(hwndRichEdit, EM_STREAMIN,
3837 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3838 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3839 result = strcmp (buffer,"testSome text");
3840 ok (result == 0,
3841 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3843 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3844 ok (result == TRUE,
3845 "EM_STREAMIN with SFF_SELECTION but no selection set "
3846 "should create an undo\n");
3848 /* StreamIn, SFF_SELECTION, with a selection */
3849 es.dwCookie = nCallbackCount;
3850 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3851 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3852 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
3853 SendMessage(hwndRichEdit, EM_STREAMIN,
3854 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3855 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3856 result = strcmp (buffer,"Sometesttext");
3857 ok (result == 0,
3858 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3860 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3861 ok (result == TRUE,
3862 "EM_STREAMIN with SFF_SELECTION and selection set "
3863 "should create an undo\n");
3867 static BOOL is_em_settextex_supported(HWND hwnd)
3869 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
3870 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
3873 static void test_unicode_conversions(void)
3875 static const WCHAR tW[] = {'t',0};
3876 static const WCHAR teW[] = {'t','e',0};
3877 static const WCHAR textW[] = {'t','e','s','t',0};
3878 static const char textA[] = "test";
3879 char bufA[64];
3880 WCHAR bufW[64];
3881 HWND hwnd;
3882 int is_win9x, em_settextex_supported, ret;
3884 is_win9x = GetVersion() & 0x80000000;
3886 #define set_textA(hwnd, wm_set_text, txt) \
3887 do { \
3888 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
3889 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3890 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3891 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3892 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
3893 } while(0)
3894 #define expect_textA(hwnd, wm_get_text, txt) \
3895 do { \
3896 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3897 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3898 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3899 memset(bufA, 0xAA, sizeof(bufA)); \
3900 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3901 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3902 ret = lstrcmpA(bufA, txt); \
3903 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
3904 } while(0)
3906 #define set_textW(hwnd, wm_set_text, txt) \
3907 do { \
3908 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
3909 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3910 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3911 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3912 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
3913 } while(0)
3914 #define expect_textW(hwnd, wm_get_text, txt) \
3915 do { \
3916 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
3917 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3918 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3919 memset(bufW, 0xAA, sizeof(bufW)); \
3920 if (is_win9x) \
3922 assert(wm_get_text == EM_GETTEXTEX); \
3923 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3924 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3926 else \
3928 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3929 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
3931 ret = lstrcmpW(bufW, txt); \
3932 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
3933 } while(0)
3934 #define expect_empty(hwnd, wm_get_text) \
3935 do { \
3936 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3937 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
3938 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3939 memset(bufA, 0xAA, sizeof(bufA)); \
3940 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3941 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
3942 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
3943 } while(0)
3945 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3946 0, 0, 200, 60, 0, 0, 0, 0);
3947 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3949 ret = IsWindowUnicode(hwnd);
3950 if (is_win9x)
3951 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
3952 else
3953 ok(ret, "RichEdit20W should be unicode under NT\n");
3955 /* EM_SETTEXTEX is supported starting from version 3.0 */
3956 em_settextex_supported = is_em_settextex_supported(hwnd);
3957 trace("EM_SETTEXTEX is %ssupported on this platform\n",
3958 em_settextex_supported ? "" : "NOT ");
3960 expect_empty(hwnd, WM_GETTEXT);
3961 expect_empty(hwnd, EM_GETTEXTEX);
3963 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
3964 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3965 expect_textA(hwnd, WM_GETTEXT, "t");
3966 expect_textA(hwnd, EM_GETTEXTEX, "t");
3967 expect_textW(hwnd, EM_GETTEXTEX, tW);
3969 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
3970 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3971 expect_textA(hwnd, WM_GETTEXT, "te");
3972 expect_textA(hwnd, EM_GETTEXTEX, "te");
3973 expect_textW(hwnd, EM_GETTEXTEX, teW);
3975 set_textA(hwnd, WM_SETTEXT, NULL);
3976 expect_empty(hwnd, WM_GETTEXT);
3977 expect_empty(hwnd, EM_GETTEXTEX);
3979 if (is_win9x)
3980 set_textA(hwnd, WM_SETTEXT, textW);
3981 else
3982 set_textA(hwnd, WM_SETTEXT, textA);
3983 expect_textA(hwnd, WM_GETTEXT, textA);
3984 expect_textA(hwnd, EM_GETTEXTEX, textA);
3985 expect_textW(hwnd, EM_GETTEXTEX, textW);
3987 if (em_settextex_supported)
3989 set_textA(hwnd, EM_SETTEXTEX, textA);
3990 expect_textA(hwnd, WM_GETTEXT, textA);
3991 expect_textA(hwnd, EM_GETTEXTEX, textA);
3992 expect_textW(hwnd, EM_GETTEXTEX, textW);
3995 if (!is_win9x)
3997 set_textW(hwnd, WM_SETTEXT, textW);
3998 expect_textW(hwnd, WM_GETTEXT, textW);
3999 expect_textA(hwnd, WM_GETTEXT, textA);
4000 expect_textW(hwnd, EM_GETTEXTEX, textW);
4001 expect_textA(hwnd, EM_GETTEXTEX, textA);
4003 if (em_settextex_supported)
4005 set_textW(hwnd, EM_SETTEXTEX, textW);
4006 expect_textW(hwnd, WM_GETTEXT, textW);
4007 expect_textA(hwnd, WM_GETTEXT, textA);
4008 expect_textW(hwnd, EM_GETTEXTEX, textW);
4009 expect_textA(hwnd, EM_GETTEXTEX, textA);
4012 DestroyWindow(hwnd);
4014 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
4015 0, 0, 200, 60, 0, 0, 0, 0);
4016 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4018 ret = IsWindowUnicode(hwnd);
4019 ok(!ret, "RichEdit20A should NOT be unicode\n");
4021 set_textA(hwnd, WM_SETTEXT, textA);
4022 expect_textA(hwnd, WM_GETTEXT, textA);
4023 expect_textA(hwnd, EM_GETTEXTEX, textA);
4024 expect_textW(hwnd, EM_GETTEXTEX, textW);
4026 if (em_settextex_supported)
4028 set_textA(hwnd, EM_SETTEXTEX, textA);
4029 expect_textA(hwnd, WM_GETTEXT, textA);
4030 expect_textA(hwnd, EM_GETTEXTEX, textA);
4031 expect_textW(hwnd, EM_GETTEXTEX, textW);
4034 if (!is_win9x)
4036 set_textW(hwnd, WM_SETTEXT, textW);
4037 expect_textW(hwnd, WM_GETTEXT, textW);
4038 expect_textA(hwnd, WM_GETTEXT, textA);
4039 expect_textW(hwnd, EM_GETTEXTEX, textW);
4040 expect_textA(hwnd, EM_GETTEXTEX, textA);
4042 if (em_settextex_supported)
4044 set_textW(hwnd, EM_SETTEXTEX, textW);
4045 expect_textW(hwnd, WM_GETTEXT, textW);
4046 expect_textA(hwnd, WM_GETTEXT, textA);
4047 expect_textW(hwnd, EM_GETTEXTEX, textW);
4048 expect_textA(hwnd, EM_GETTEXTEX, textA);
4051 DestroyWindow(hwnd);
4054 static void test_WM_CHAR(void)
4056 HWND hwnd;
4057 int ret;
4058 const char * char_list = "abc\rabc\r";
4059 const char * expected_content_single = "abcabc";
4060 const char * expected_content_multi = "abc\r\nabc\r\n";
4061 char buffer[64] = {0};
4062 const char * p;
4064 /* single-line control must IGNORE carriage returns */
4065 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4066 0, 0, 200, 60, 0, 0, 0, 0);
4067 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4069 p = char_list;
4070 while (*p != '\0') {
4071 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4072 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4073 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4074 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4075 p++;
4078 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4079 ret = strcmp(buffer, expected_content_single);
4080 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4082 DestroyWindow(hwnd);
4084 /* multi-line control inserts CR normally */
4085 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4086 0, 0, 200, 60, 0, 0, 0, 0);
4087 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4089 p = char_list;
4090 while (*p != '\0') {
4091 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
4092 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
4093 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
4094 SendMessageA(hwnd, WM_KEYUP, *p, 1);
4095 p++;
4098 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4099 ret = strcmp(buffer, expected_content_multi);
4100 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4102 DestroyWindow(hwnd);
4105 static void test_EM_GETTEXTLENGTHEX(void)
4107 HWND hwnd;
4108 GETTEXTLENGTHEX gtl;
4109 int ret;
4110 const char * base_string = "base string";
4111 const char * test_string = "a\nb\n\n\r\n";
4112 const char * test_string_after = "a";
4113 const char * test_string_2 = "a\rtest\rstring";
4114 char buffer[64] = {0};
4116 /* single line */
4117 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4118 0, 0, 200, 60, 0, 0, 0, 0);
4119 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4121 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4122 gtl.codepage = CP_ACP;
4123 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4124 ok(ret == 0, "ret %d\n",ret);
4126 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4127 gtl.codepage = CP_ACP;
4128 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4129 ok(ret == 0, "ret %d\n",ret);
4131 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4133 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4134 gtl.codepage = CP_ACP;
4135 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4136 ok(ret == strlen(base_string), "ret %d\n",ret);
4138 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4139 gtl.codepage = CP_ACP;
4140 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4141 ok(ret == strlen(base_string), "ret %d\n",ret);
4143 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4145 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4146 gtl.codepage = CP_ACP;
4147 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4148 ok(ret == 1, "ret %d\n",ret);
4150 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4151 gtl.codepage = CP_ACP;
4152 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4153 ok(ret == 1, "ret %d\n",ret);
4155 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4156 ret = strcmp(buffer, test_string_after);
4157 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4159 DestroyWindow(hwnd);
4161 /* multi line */
4162 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
4163 0, 0, 200, 60, 0, 0, 0, 0);
4164 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4166 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4167 gtl.codepage = CP_ACP;
4168 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4169 ok(ret == 0, "ret %d\n",ret);
4171 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4172 gtl.codepage = CP_ACP;
4173 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4174 ok(ret == 0, "ret %d\n",ret);
4176 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4178 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4179 gtl.codepage = CP_ACP;
4180 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4181 ok(ret == strlen(base_string), "ret %d\n",ret);
4183 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4184 gtl.codepage = CP_ACP;
4185 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4186 ok(ret == strlen(base_string), "ret %d\n",ret);
4188 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
4190 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4191 gtl.codepage = CP_ACP;
4192 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4193 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
4195 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4196 gtl.codepage = CP_ACP;
4197 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4198 ok(ret == strlen(test_string_2), "ret %d\n",ret);
4200 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4202 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4203 gtl.codepage = CP_ACP;
4204 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4205 ok(ret == 10, "ret %d\n",ret);
4207 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4208 gtl.codepage = CP_ACP;
4209 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4210 ok(ret == 6, "ret %d\n",ret);
4212 DestroyWindow(hwnd);
4216 /* globals that parent and child access when checking event masks & notifications */
4217 static HWND eventMaskEditHwnd = 0;
4218 static int queriedEventMask;
4219 static int watchForEventMask = 0;
4221 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
4222 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4224 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
4226 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4228 return DefWindowProcA(hwnd, message, wParam, lParam);
4231 /* test event masks in combination with WM_COMMAND */
4232 static void test_eventMask(void)
4234 HWND parent;
4235 int ret;
4236 WNDCLASSA cls;
4237 const char text[] = "foo bar\n";
4238 int eventMask;
4240 /* register class to capture WM_COMMAND */
4241 cls.style = 0;
4242 cls.lpfnWndProc = ParentMsgCheckProcA;
4243 cls.cbClsExtra = 0;
4244 cls.cbWndExtra = 0;
4245 cls.hInstance = GetModuleHandleA(0);
4246 cls.hIcon = 0;
4247 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4248 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4249 cls.lpszMenuName = NULL;
4250 cls.lpszClassName = "EventMaskParentClass";
4251 if(!RegisterClassA(&cls)) assert(0);
4253 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4254 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4255 ok (parent != 0, "Failed to create parent window\n");
4257 eventMaskEditHwnd = new_richedit(parent);
4258 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
4260 eventMask = ENM_CHANGE | ENM_UPDATE;
4261 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
4262 ok(ret == ENM_NONE, "wrong event mask\n");
4263 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4264 ok(ret == eventMask, "failed to set event mask\n");
4266 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
4267 queriedEventMask = 0; /* initialize to something other than we expect */
4268 watchForEventMask = EN_CHANGE;
4269 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
4270 ok(ret == TRUE, "failed to set text\n");
4271 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
4272 notification in response to WM_SETTEXT */
4273 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
4274 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
4278 static int received_WM_NOTIFY = 0;
4279 static int modify_at_WM_NOTIFY = 0;
4280 static HWND hwndRichedit_WM_NOTIFY;
4282 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4284 if(message == WM_NOTIFY)
4286 received_WM_NOTIFY = 1;
4287 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
4289 return DefWindowProcA(hwnd, message, wParam, lParam);
4292 static void test_WM_NOTIFY(void)
4294 HWND parent;
4295 WNDCLASSA cls;
4296 CHARFORMAT2 cf2;
4298 /* register class to capture WM_NOTIFY */
4299 cls.style = 0;
4300 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
4301 cls.cbClsExtra = 0;
4302 cls.cbWndExtra = 0;
4303 cls.hInstance = GetModuleHandleA(0);
4304 cls.hIcon = 0;
4305 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4306 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4307 cls.lpszMenuName = NULL;
4308 cls.lpszClassName = "WM_NOTIFY_ParentClass";
4309 if(!RegisterClassA(&cls)) assert(0);
4311 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4312 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4313 ok (parent != 0, "Failed to create parent window\n");
4315 hwndRichedit_WM_NOTIFY = new_richedit(parent);
4316 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
4318 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
4320 /* Notifications for selection change should only be sent when selection
4321 actually changes. EM_SETCHARFORMAT is one message that calls
4322 ME_CommitUndo, which should check whether message should be sent */
4323 received_WM_NOTIFY = 0;
4324 cf2.cbSize = sizeof(CHARFORMAT2);
4325 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4326 (LPARAM) &cf2);
4327 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4328 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4329 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
4330 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4332 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
4333 already at 0. */
4334 received_WM_NOTIFY = 0;
4335 modify_at_WM_NOTIFY = 0;
4336 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4337 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4338 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4340 received_WM_NOTIFY = 0;
4341 modify_at_WM_NOTIFY = 0;
4342 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
4343 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4345 received_WM_NOTIFY = 0;
4346 modify_at_WM_NOTIFY = 0;
4347 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4348 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4349 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4351 DestroyWindow(hwndRichedit_WM_NOTIFY);
4352 DestroyWindow(parent);
4355 static void simulate_typing_characters(HWND hwnd, const char* szChars)
4357 int ret;
4359 while (*szChars != '\0') {
4360 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
4361 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
4362 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
4363 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
4364 szChars++;
4368 static void test_undo_coalescing(void)
4370 HWND hwnd;
4371 int result;
4372 char buffer[64] = {0};
4374 /* multi-line control inserts CR normally */
4375 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
4376 0, 0, 200, 60, 0, 0, 0, 0);
4377 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4379 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4380 ok (result == FALSE, "Can undo after window creation.\n");
4381 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4382 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
4383 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4384 ok (result == FALSE, "Can redo after window creation.\n");
4385 result = SendMessage(hwnd, EM_REDO, 0, 0);
4386 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
4388 /* Test the effect of arrows keys during typing on undo transactions*/
4389 simulate_typing_characters(hwnd, "one two three");
4390 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
4391 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
4392 simulate_typing_characters(hwnd, " four five six");
4394 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4395 ok (result == FALSE, "Can redo before anything is undone.\n");
4396 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4397 ok (result == TRUE, "Cannot undo typed characters.\n");
4398 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4399 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
4400 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4401 ok (result == TRUE, "Cannot redo after undo.\n");
4402 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4403 result = strcmp(buffer, "one two three");
4404 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4406 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
4407 ok (result == TRUE, "Cannot undo typed characters.\n");
4408 result = SendMessage(hwnd, WM_UNDO, 0, 0);
4409 ok (result == TRUE, "Failed to undo typed characters.\n");
4410 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4411 result = strcmp(buffer, "");
4412 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4414 /* Test the effect of focus changes during typing on undo transactions*/
4415 simulate_typing_characters(hwnd, "one two three");
4416 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4417 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
4418 SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
4419 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
4420 simulate_typing_characters(hwnd, " four five six");
4421 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4422 ok (result == TRUE, "Failed to undo typed characters.\n");
4423 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4424 result = strcmp(buffer, "one two three");
4425 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4427 /* Test the effect of the back key during typing on undo transactions */
4428 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4429 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
4430 ok (result == TRUE, "Failed to clear the text.\n");
4431 simulate_typing_characters(hwnd, "one two threa");
4432 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
4433 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
4434 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
4435 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
4436 simulate_typing_characters(hwnd, "e four five six");
4437 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4438 ok (result == TRUE, "Failed to undo typed characters.\n");
4439 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4440 result = strcmp(buffer, "");
4441 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4443 /* Test the effect of the delete key during typing on undo transactions */
4444 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4445 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
4446 ok(result == TRUE, "Failed to set the text.\n");
4447 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
4448 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
4449 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
4450 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
4451 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
4452 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4453 ok (result == TRUE, "Failed to undo typed characters.\n");
4454 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4455 result = strcmp(buffer, "acd");
4456 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
4457 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4458 ok (result == TRUE, "Failed to undo typed characters.\n");
4459 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4460 result = strcmp(buffer, "abcd");
4461 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
4463 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
4464 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
4465 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
4466 ok (result == TRUE, "Failed to clear the text.\n");
4467 simulate_typing_characters(hwnd, "one two three");
4468 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
4469 ok (result == 0, "expected %d but got %d\n", 0, result);
4470 simulate_typing_characters(hwnd, " four five six");
4471 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4472 ok (result == TRUE, "Failed to undo typed characters.\n");
4473 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4474 result = strcmp(buffer, "one two three");
4475 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
4476 result = SendMessage(hwnd, EM_UNDO, 0, 0);
4477 ok (result == TRUE, "Failed to undo typed characters.\n");
4478 ok (result == TRUE, "Failed to undo typed characters.\n");
4479 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4480 result = strcmp(buffer, "");
4481 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
4483 DestroyWindow(hwnd);
4486 /* Used to simulate a Ctrl-Arrow Key press. */
4487 #define SEND_CTRL_EXT_KEY(hwnd, vk, scancode) \
4488 keybd_event(VK_CONTROL, 0x1d, 0, 0);\
4489 keybd_event(vk, scancode, KEYEVENTF_EXTENDEDKEY, 0);\
4490 keybd_event(vk, scancode | 0x80, KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY, 0);\
4491 keybd_event(VK_CONTROL, 0x1d | 0x80, KEYEVENTF_KEYUP, 0); \
4492 while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) { \
4493 TranslateMessage(&msg); \
4494 DispatchMessage(&msg); \
4496 #define SEND_CTRL_LEFT(hwnd) SEND_CTRL_EXT_KEY(hwnd, VK_LEFT, 0x4b)
4497 #define SEND_CTRL_RIGHT(hwnd) SEND_CTRL_EXT_KEY(hwnd, VK_RIGHT, 0x4d)
4499 static void test_word_movement(){
4500 HWND hwnd;
4501 int result;
4502 int sel_start, sel_end;
4503 MSG msg;
4505 /* multi-line control inserts CR normally */
4506 hwnd = new_richedit(NULL);
4508 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)hwnd, 0);
4509 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
4510 ok (result == TRUE, "Failed to clear the text.\n");
4511 SendMessage(hwnd, EM_SETSEL, 0, 0);
4512 /* |one two three */
4514 SEND_CTRL_RIGHT(hwnd)
4515 /* one |two three */
4516 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4517 ok(sel_start == sel_end, "Selection should be empty\n");
4518 ok(sel_start == 4, "Cursur is at %d instead of %d\n", sel_start, 4);
4520 SEND_CTRL_RIGHT(hwnd)
4521 /* one two |three */
4522 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4523 ok(sel_start == sel_end, "Selection should be empty\n");
4524 ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4526 SEND_CTRL_LEFT(hwnd)
4527 /* one |two three */
4528 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4529 ok(sel_start == sel_end, "Selection should be empty\n");
4530 ok(sel_start == 4, "Cursur is at %d instead of %d\n", sel_start, 4);
4532 SEND_CTRL_LEFT(hwnd)
4533 /* |one two three */
4534 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4535 ok(sel_start == sel_end, "Selection should be empty\n");
4536 ok(sel_start == 0, "Cursur is at %d instead of %d\n", sel_start, 0);
4538 SendMessage(hwnd, EM_SETSEL, 8, 8);
4539 /* one two | three */
4540 SEND_CTRL_RIGHT(hwnd)
4541 /* one two |three */
4542 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4543 ok(sel_start == sel_end, "Selection should be empty\n");
4544 ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4546 SendMessage(hwnd, EM_SETSEL, 11, 11);
4547 /* one two th|ree */
4548 SEND_CTRL_LEFT(hwnd)
4549 /* one two |three */
4550 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4551 ok(sel_start == sel_end, "Selection should be empty\n");
4552 ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
4554 DestroyWindow(hwnd);
4557 START_TEST( editor )
4559 MSG msg;
4560 time_t end;
4562 /* Must explicitly LoadLibrary(). The test has no references to functions in
4563 * RICHED20.DLL, so the linker doesn't actually link to it. */
4564 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
4565 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
4566 test_WM_CHAR();
4567 test_EM_FINDTEXT();
4568 test_EM_GETLINE();
4569 test_EM_POSFROMCHAR();
4570 test_EM_SCROLLCARET();
4571 test_EM_SCROLL();
4572 test_WM_SETTEXT();
4573 test_EM_LINELENGTH();
4574 test_EM_SETCHARFORMAT();
4575 test_EM_SETTEXTMODE();
4576 test_TM_PLAINTEXT();
4577 test_EM_SETOPTIONS();
4578 test_WM_GETTEXT();
4579 test_EM_GETTEXTRANGE();
4580 test_EM_GETSELTEXT();
4581 test_EM_SETUNDOLIMIT();
4582 test_ES_PASSWORD();
4583 test_EM_SETTEXTEX();
4584 test_EM_LIMITTEXT();
4585 test_EM_EXLIMITTEXT();
4586 test_EM_GETLIMITTEXT();
4587 test_WM_SETFONT();
4588 test_EM_GETMODIFY();
4589 test_EM_EXSETSEL();
4590 test_WM_PASTE();
4591 test_EM_STREAMIN();
4592 test_EM_STREAMOUT();
4593 test_EM_StreamIn_Undo();
4594 test_EM_FORMATRANGE();
4595 test_unicode_conversions();
4596 test_EM_GETTEXTLENGTHEX();
4597 test_EM_REPLACESEL(1);
4598 test_EM_REPLACESEL(0);
4599 test_WM_NOTIFY();
4600 test_EM_AUTOURLDETECT();
4601 test_eventMask();
4602 test_undo_coalescing();
4603 test_word_movement();
4605 /* Set the environment variable WINETEST_RICHED20 to keep windows
4606 * responsive and open for 30 seconds. This is useful for debugging.
4608 * The message pump uses PeekMessage() to empty the queue and then sleeps for
4609 * 50ms before retrying the queue. */
4610 end = time(NULL) + 30;
4611 if (getenv( "WINETEST_RICHED20" )) {
4612 while (time(NULL) < end) {
4613 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
4614 TranslateMessage(&msg);
4615 DispatchMessage(&msg);
4616 } else {
4617 Sleep(50);
4622 OleFlushClipboard();
4623 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());