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
33 #include <wine/test.h>
35 static HMODULE hmoduleRichEdit;
37 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
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());
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";
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[] = {
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},
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},
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},
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) {
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,
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,
177 for (i = 0; i < num_tests; i++) {
178 if (find[i]._todo_wine) {
180 check_EM_FINDTEXT(hwnd, name, &find[i], i);
181 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
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);
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);
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,
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 {
237 {0, 10, "foo bar\r"},
242 /* Buffer smaller than line length */
248 static void test_EM_GETLINE(void)
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"
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++)
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,
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),
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");
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);
302 int offset_test[10][2] = {
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)
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);
336 static void move_cursor(HWND hwnd, long charindex)
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)
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 "
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);
405 unsigned int height = 0;
406 unsigned int xpos = 0;
408 /* Fill the control to lines to ensure that most of them are offscreen */
409 for (i = 0; i < 50; i++)
411 /* Do not modify the string; it is exactly 16 characters long. */
412 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
413 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
417 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
418 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
419 Richedit 3.0 accepts either of the above API conventions.
422 /* Testing Richedit 2.0 API format */
424 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
425 Since all lines are identical and drawn with the same font,
426 they should have the same height... right?
428 for (i = 0; i < 50; i++)
430 /* All the lines are 16 characters long */
431 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
434 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
436 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
438 xpos = LOWORD(result);
442 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
443 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
444 height = HIWORD(result);
448 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
449 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
453 /* Testing position at end of text */
454 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
455 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
456 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
458 /* Testing position way past end of text */
459 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
460 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
461 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
463 DestroyWindow(hwndRichEdit);
466 static void test_EM_SETCHARFORMAT(void)
468 HWND hwndRichEdit = new_richedit(NULL);
471 int tested_effects[] = {
485 /* Invalid flags, CHARFORMAT2 structure blanked out */
486 memset(&cf2, 0, sizeof(cf2));
487 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
489 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
491 /* A valid flag, CHARFORMAT2 structure blanked out */
492 memset(&cf2, 0, sizeof(cf2));
493 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
495 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
497 /* A valid flag, CHARFORMAT2 structure blanked out */
498 memset(&cf2, 0, sizeof(cf2));
499 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
501 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
503 /* A valid flag, CHARFORMAT2 structure blanked out */
504 memset(&cf2, 0, sizeof(cf2));
505 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
507 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
509 /* A valid flag, CHARFORMAT2 structure blanked out */
510 memset(&cf2, 0, sizeof(cf2));
511 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
513 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
515 /* Invalid flags, CHARFORMAT2 structure minimally filled */
516 memset(&cf2, 0, sizeof(cf2));
517 cf2.cbSize = sizeof(CHARFORMAT2);
518 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
520 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
522 /* A valid flag, CHARFORMAT2 structure minimally filled */
523 memset(&cf2, 0, sizeof(cf2));
524 cf2.cbSize = sizeof(CHARFORMAT2);
525 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
527 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
529 /* A valid flag, CHARFORMAT2 structure minimally filled */
530 memset(&cf2, 0, sizeof(cf2));
531 cf2.cbSize = sizeof(CHARFORMAT2);
532 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
534 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
536 /* A valid flag, CHARFORMAT2 structure minimally filled */
537 memset(&cf2, 0, sizeof(cf2));
538 cf2.cbSize = sizeof(CHARFORMAT2);
539 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
541 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
543 /* A valid flag, CHARFORMAT2 structure minimally filled */
544 memset(&cf2, 0, sizeof(cf2));
545 cf2.cbSize = sizeof(CHARFORMAT2);
546 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
548 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
550 cf2.cbSize = sizeof(CHARFORMAT2);
551 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
554 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
555 cf2.cbSize = sizeof(CHARFORMAT2);
556 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
558 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
559 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
561 /* wParam==0 is default char format, does not set modify */
562 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
563 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
564 ok(rc == 0, "Text marked as modified, expected not modified!\n");
565 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
566 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
567 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
568 ok(rc == 0, "Text marked as modified, expected not modified!\n");
570 /* wParam==SCF_SELECTION sets modify if nonempty selection */
571 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
572 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
573 ok(rc == 0, "Text marked as modified, expected not modified!\n");
574 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
575 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
576 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
577 ok(rc == 0, "Text marked as modified, expected not modified!\n");
579 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
580 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
581 ok(rc == 0, "Text marked as modified, expected not modified!\n");
582 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
583 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
584 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
585 ok(rc == 0, "Text marked as modified, expected not modified!\n");
586 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
587 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
588 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
589 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
590 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
592 /* wParam==SCF_ALL sets modify regardless of whether text is present */
593 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
594 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
595 ok(rc == 0, "Text marked as modified, expected not modified!\n");
596 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
597 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
598 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
599 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
601 DestroyWindow(hwndRichEdit);
603 /* EM_GETCHARFORMAT tests */
604 for (i = 0; tested_effects[i]; i++)
606 hwndRichEdit = new_richedit(NULL);
607 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
609 memset(&cf2, 0, sizeof(CHARFORMAT2));
610 cf2.cbSize = sizeof(CHARFORMAT2);
611 cf2.dwMask = tested_effects[i];
612 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
613 cf2.dwMask = CFM_SUPERSCRIPT;
614 cf2.dwEffects = tested_effects[i];
615 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
616 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
618 memset(&cf2, 0, sizeof(CHARFORMAT2));
619 cf2.cbSize = sizeof(CHARFORMAT2);
620 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
621 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
622 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
623 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
625 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
626 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
627 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
628 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
630 memset(&cf2, 0, sizeof(CHARFORMAT2));
631 cf2.cbSize = sizeof(CHARFORMAT2);
632 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
633 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
634 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
635 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
637 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
638 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
639 ok((cf2.dwEffects & tested_effects[i]) == 0,
640 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
642 memset(&cf2, 0, sizeof(CHARFORMAT2));
643 cf2.cbSize = sizeof(CHARFORMAT2);
644 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
645 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
646 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
647 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
649 (cf2.dwMask & tested_effects[i]) == 0),
650 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
651 ok((cf2.dwEffects & tested_effects[i]) == 0,
652 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
654 DestroyWindow(hwndRichEdit);
657 for (i = 0; tested_effects[i]; i++)
659 hwndRichEdit = new_richedit(NULL);
660 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
662 memset(&cf2, 0, sizeof(CHARFORMAT2));
663 cf2.cbSize = sizeof(CHARFORMAT2);
664 cf2.dwMask = tested_effects[i];
665 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
666 cf2.dwMask = CFM_SUPERSCRIPT;
667 cf2.dwEffects = tested_effects[i];
668 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
669 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
671 memset(&cf2, 0, sizeof(CHARFORMAT2));
672 cf2.cbSize = sizeof(CHARFORMAT2);
673 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
674 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
675 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
676 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
678 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
679 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
680 ok((cf2.dwEffects & tested_effects[i]) == 0,
681 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
683 memset(&cf2, 0, sizeof(CHARFORMAT2));
684 cf2.cbSize = sizeof(CHARFORMAT2);
685 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
686 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
687 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
688 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
690 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
691 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
692 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
693 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
695 memset(&cf2, 0, sizeof(CHARFORMAT2));
696 cf2.cbSize = sizeof(CHARFORMAT2);
697 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
698 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
699 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
700 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
702 (cf2.dwMask & tested_effects[i]) == 0),
703 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
704 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
705 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
707 DestroyWindow(hwndRichEdit);
710 /* Effects applied on an empty selection should take effect when selection is
711 replaced with text */
712 hwndRichEdit = new_richedit(NULL);
713 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
714 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
716 memset(&cf2, 0, sizeof(CHARFORMAT2));
717 cf2.cbSize = sizeof(CHARFORMAT2);
718 cf2.dwMask = CFM_BOLD;
719 cf2.dwEffects = CFE_BOLD;
720 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
722 /* Selection is now nonempty */
723 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
725 memset(&cf2, 0, sizeof(CHARFORMAT2));
726 cf2.cbSize = sizeof(CHARFORMAT2);
727 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
728 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
730 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
731 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
732 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
733 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
736 /* Set two effects on an empty selection */
737 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
738 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
740 memset(&cf2, 0, sizeof(CHARFORMAT2));
741 cf2.cbSize = sizeof(CHARFORMAT2);
742 cf2.dwMask = CFM_BOLD;
743 cf2.dwEffects = CFE_BOLD;
744 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
745 cf2.dwMask = CFM_ITALIC;
746 cf2.dwEffects = CFE_ITALIC;
747 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
749 /* Selection is now nonempty */
750 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
752 memset(&cf2, 0, sizeof(CHARFORMAT2));
753 cf2.cbSize = sizeof(CHARFORMAT2);
754 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
755 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
757 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
758 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
759 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
760 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
762 /* Setting the (empty) selection to exactly the same place as before should
763 NOT clear the insertion style! */
764 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
765 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
767 memset(&cf2, 0, sizeof(CHARFORMAT2));
768 cf2.cbSize = sizeof(CHARFORMAT2);
769 cf2.dwMask = CFM_BOLD;
770 cf2.dwEffects = CFE_BOLD;
771 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
773 /* Empty selection in same place, insert style should NOT be forgotten here. */
774 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
776 /* Selection is now nonempty */
777 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
779 memset(&cf2, 0, sizeof(CHARFORMAT2));
780 cf2.cbSize = sizeof(CHARFORMAT2);
781 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
782 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
784 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
785 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
786 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
787 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
789 /* Ditto with EM_EXSETSEL */
790 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
791 cr.cpMin = 2; cr.cpMax = 2;
792 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
794 memset(&cf2, 0, sizeof(CHARFORMAT2));
795 cf2.cbSize = sizeof(CHARFORMAT2);
796 cf2.dwMask = CFM_BOLD;
797 cf2.dwEffects = CFE_BOLD;
798 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
800 /* Empty selection in same place, insert style should NOT be forgotten here. */
801 cr.cpMin = 2; cr.cpMax = 2;
802 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
804 /* Selection is now nonempty */
805 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
807 memset(&cf2, 0, sizeof(CHARFORMAT2));
808 cf2.cbSize = sizeof(CHARFORMAT2);
809 cr.cpMin = 2; cr.cpMax = 6;
810 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
811 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
813 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
814 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
815 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
816 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
818 DestroyWindow(hwndRichEdit);
821 static void test_EM_SETTEXTMODE(void)
823 HWND hwndRichEdit = new_richedit(NULL);
824 CHARFORMAT2 cf2, cf2test;
828 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
829 /*Insert text into the control*/
831 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
833 /*Attempt to change the control to plain text mode*/
834 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
835 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
837 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
838 If rich text is pasted, it should have the same formatting as the rest
839 of the text in the control*/
842 *NOTE: If the default text was already italicized, the test will simply
843 reverse; in other words, it will copy a regular "wine" into a plain
844 text window that uses an italicized format*/
845 cf2.cbSize = sizeof(CHARFORMAT2);
846 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
849 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
850 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
852 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
853 ok(rc == 0, "Text marked as modified, expected not modified!\n");
855 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
856 however, SCF_ALL has been implemented*/
857 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
858 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
860 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
861 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
863 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
865 /*Select the string "wine"*/
868 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
870 /*Copy the italicized "wine" to the clipboard*/
871 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
873 /*Reset the formatting to default*/
874 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
875 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
876 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
878 /*Clear the text in the control*/
879 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
881 /*Switch to Plain Text Mode*/
882 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
883 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
885 /*Input "wine" again in normal format*/
886 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
888 /*Paste the italicized "wine" into the control*/
889 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
891 /*Select a character from the first "wine" string*/
894 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
896 /*Retrieve its formatting*/
897 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
900 /*Select a character from the second "wine" string*/
903 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
905 /*Retrieve its formatting*/
906 cf2test.cbSize = sizeof(CHARFORMAT2);
907 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
910 /*Compare the two formattings*/
911 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
912 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
913 cf2.dwEffects, cf2test.dwEffects);
914 /*Test TM_RICHTEXT by: switching back to Rich Text mode
915 printing "wine" in the current format(normal)
916 pasting "wine" from the clipboard(italicized)
917 comparing the two formats(should differ)*/
919 /*Attempt to switch with text in control*/
920 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
921 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
924 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
926 /*Switch into Rich Text mode*/
927 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
928 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
930 /*Print "wine" in normal formatting into the control*/
931 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
933 /*Paste italicized "wine" into the control*/
934 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
936 /*Select text from the first "wine" string*/
939 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
941 /*Retrieve its formatting*/
942 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
945 /*Select text from the second "wine" string*/
948 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
950 /*Retrieve its formatting*/
951 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
954 /*Test that the two formattings are not the same*/
955 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
956 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
957 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
959 DestroyWindow(hwndRichEdit);
962 static void test_TM_PLAINTEXT(void)
964 /*Tests plain text properties*/
966 HWND hwndRichEdit = new_richedit(NULL);
967 CHARFORMAT2 cf2, cf2test;
971 /*Switch to plain text mode*/
973 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
974 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
976 /*Fill control with text*/
978 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
980 /*Select some text and bold it*/
984 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
985 cf2.cbSize = sizeof(CHARFORMAT2);
986 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
989 cf2.dwMask = CFM_BOLD | cf2.dwMask;
990 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
992 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
993 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
995 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
996 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
998 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
999 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1001 /*Get the formatting of those characters*/
1003 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1005 /*Get the formatting of some other characters*/
1006 cf2test.cbSize = sizeof(CHARFORMAT2);
1009 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1010 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1012 /*Test that they are the same as plain text allows only one formatting*/
1014 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1015 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1016 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1018 /*Fill the control with a "wine" string, which when inserted will be bold*/
1020 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1022 /*Copy the bolded "wine" string*/
1026 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1027 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1029 /*Swap back to rich text*/
1031 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1032 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1034 /*Set the default formatting to bold italics*/
1036 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1037 cf2.dwMask |= CFM_ITALIC;
1038 cf2.dwEffects ^= CFE_ITALIC;
1039 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1040 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1042 /*Set the text in the control to "wine", which will be bold and italicized*/
1044 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1046 /*Paste the plain text "wine" string, which should take the insert
1047 formatting, which at the moment is bold italics*/
1049 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1051 /*Select the first "wine" string and retrieve its formatting*/
1055 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1056 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1058 /*Select the second "wine" string and retrieve its formatting*/
1062 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1063 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1065 /*Compare the two formattings. They should be the same.*/
1067 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1068 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1069 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1070 DestroyWindow(hwndRichEdit);
1073 static void test_WM_GETTEXT(void)
1075 HWND hwndRichEdit = new_richedit(NULL);
1076 static const char text[] = "Hello. My name is RichEdit!";
1077 static const char text2[] = "Hello. My name is RichEdit!\r";
1078 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1079 char buffer[1024] = {0};
1082 /* Baseline test with normal-sized buffer */
1083 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1084 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1085 ok(result == lstrlen(buffer),
1086 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1087 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1088 result = strcmp(buffer,text);
1090 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1092 /* Test for returned value of WM_GETTEXTLENGTH */
1093 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1094 ok(result == lstrlen(text),
1095 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1096 result, lstrlen(text));
1098 /* Test for behavior in overflow case */
1099 memset(buffer, 0, 1024);
1100 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1102 result == lstrlenA(text) - 1, /* XP, win2k3 */
1103 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1104 result = strcmp(buffer,text);
1106 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1108 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1110 /* Baseline test with normal-sized buffer and carriage return */
1111 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1112 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1113 ok(result == lstrlen(buffer),
1114 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1115 result = strcmp(buffer,text2_after);
1117 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1119 /* Test for returned value of WM_GETTEXTLENGTH */
1120 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1121 ok(result == lstrlen(text2_after),
1122 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1123 result, lstrlen(text2_after));
1125 /* Test for behavior of CRLF conversion in case of overflow */
1126 memset(buffer, 0, 1024);
1127 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1129 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1130 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1131 result = strcmp(buffer,text2);
1133 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1135 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1137 DestroyWindow(hwndRichEdit);
1140 static void test_EM_GETTEXTRANGE(void)
1142 HWND hwndRichEdit = new_richedit(NULL);
1143 const char * text1 = "foo bar\r\nfoo bar";
1144 const char * text2 = "foo bar\rfoo bar";
1145 const char * expect = "bar\rfoo";
1146 char buffer[1024] = {0};
1148 TEXTRANGEA textRange;
1150 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1152 textRange.lpstrText = buffer;
1153 textRange.chrg.cpMin = 4;
1154 textRange.chrg.cpMax = 11;
1155 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1156 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1157 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1159 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1161 textRange.lpstrText = buffer;
1162 textRange.chrg.cpMin = 4;
1163 textRange.chrg.cpMax = 11;
1164 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1165 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1166 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1168 DestroyWindow(hwndRichEdit);
1171 static void test_EM_GETSELTEXT(void)
1173 HWND hwndRichEdit = new_richedit(NULL);
1174 const char * text1 = "foo bar\r\nfoo bar";
1175 const char * text2 = "foo bar\rfoo bar";
1176 const char * expect = "bar\rfoo";
1177 char buffer[1024] = {0};
1180 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1182 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1183 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1184 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1185 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1187 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1189 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1190 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1191 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1192 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1194 DestroyWindow(hwndRichEdit);
1197 /* FIXME: need to test unimplemented options and robustly test wparam */
1198 static void test_EM_SETOPTIONS(void)
1200 HWND hwndRichEdit = new_richedit(NULL);
1201 static const char text[] = "Hello. My name is RichEdit!";
1202 char buffer[1024] = {0};
1204 /* NEGATIVE TESTING - NO OPTIONS SET */
1205 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1206 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1208 /* testing no readonly by sending 'a' to the control*/
1209 SetFocus(hwndRichEdit);
1210 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1211 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1213 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1214 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1216 /* READONLY - sending 'a' to the control */
1217 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1218 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1219 SetFocus(hwndRichEdit);
1220 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1221 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1222 ok(buffer[0]==text[0],
1223 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1225 DestroyWindow(hwndRichEdit);
1228 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1230 CHARFORMAT2W text_format;
1231 text_format.cbSize = sizeof(text_format);
1232 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1233 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1234 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1237 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1239 int link_present = 0;
1241 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1243 { /* control text is url; should get CFE_LINK */
1244 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1248 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1252 static HWND new_static_wnd(HWND parent) {
1253 return new_window("Static", 0, parent);
1256 static void test_EM_AUTOURLDETECT(void)
1263 {"http://www.winehq.org", 1},
1264 {"http//winehq.org", 0},
1265 {"ww.winehq.org", 0},
1266 {"www.winehq.org", 1},
1267 {"ftp://192.168.1.1", 1},
1268 {"ftp//192.168.1.1", 0},
1269 {"mailto:your@email.com", 1},
1270 {"prospero:prosperoserver", 1},
1272 {"news:newserver", 1},
1273 {"wais:waisserver", 1}
1278 HWND hwndRichEdit, parent;
1280 /* All of the following should cause the URL to be detected */
1281 const char * templates_delim[] = {
1282 "This is some text with X on it",
1283 "This is some text with (X) on it",
1284 "This is some text with X\r on it",
1285 "This is some text with ---X--- on it",
1286 "This is some text with \"X\" on it",
1287 "This is some text with 'X' on it",
1288 "This is some text with 'X' on it",
1289 "This is some text with :X: on it",
1291 "This text ends with X",
1293 "This is some text with X) on it",
1294 "This is some text with X--- on it",
1295 "This is some text with X\" on it",
1296 "This is some text with X' on it",
1297 "This is some text with X: on it",
1299 "This is some text with (X on it",
1300 "This is some text with \rX on it",
1301 "This is some text with ---X on it",
1302 "This is some text with \"X on it",
1303 "This is some text with 'X on it",
1304 "This is some text with :X on it",
1306 /* None of these should cause the URL to be detected */
1307 const char * templates_non_delim[] = {
1308 "This is some text with |X| on it",
1309 "This is some text with *X* on it",
1310 "This is some text with /X/ on it",
1311 "This is some text with +X+ on it",
1312 "This is some text with %X% on it",
1313 "This is some text with #X# on it",
1314 "This is some text with @X@ on it",
1315 "This is some text with \\X\\ on it",
1316 "This is some text with |X on it",
1317 "This is some text with *X on it",
1318 "This is some text with /X on it",
1319 "This is some text with +X on it",
1320 "This is some text with %X on it",
1321 "This is some text with #X on it",
1322 "This is some text with @X on it",
1323 "This is some text with \\X on it",
1325 /* All of these cause the URL detection to be extended by one more byte,
1326 thus demonstrating that the tested character is considered as part
1328 const char * templates_xten_delim[] = {
1329 "This is some text with X| on it",
1330 "This is some text with X* on it",
1331 "This is some text with X/ on it",
1332 "This is some text with X+ on it",
1333 "This is some text with X% on it",
1334 "This is some text with X# on it",
1335 "This is some text with X@ on it",
1336 "This is some text with X\\ on it",
1341 parent = new_static_wnd(NULL);
1342 hwndRichEdit = new_richedit(parent);
1343 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1344 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1345 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1346 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1347 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1348 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1349 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1350 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1351 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1352 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1353 /* for each url, check the text to see if CFE_LINK effect is present */
1354 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1356 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1357 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1358 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1360 /* Link detection should happen immediately upon WM_SETTEXT */
1361 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1362 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1363 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1365 DestroyWindow(hwndRichEdit);
1367 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1368 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1369 hwndRichEdit = new_richedit(parent);
1371 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1376 at_pos = strchr(templates_delim[j], 'X');
1377 at_offset = at_pos - templates_delim[j];
1378 strncpy(buffer, templates_delim[j], at_offset);
1379 buffer[at_offset] = '\0';
1380 strcat(buffer, urls[i].text);
1381 strcat(buffer, templates_delim[j] + at_offset + 1);
1382 end_offset = at_offset + strlen(urls[i].text);
1384 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1385 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1387 /* This assumes no templates start with the URL itself, and that they
1388 have at least two characters before the URL text */
1389 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1390 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1391 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1392 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1393 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1394 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1398 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1399 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1400 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1401 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1405 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1406 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1407 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1408 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1410 if (buffer[end_offset] != '\0')
1412 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1413 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1414 if (buffer[end_offset +1] != '\0')
1416 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1417 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1422 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1427 at_pos = strchr(templates_non_delim[j], 'X');
1428 at_offset = at_pos - templates_non_delim[j];
1429 strncpy(buffer, templates_non_delim[j], at_offset);
1430 buffer[at_offset] = '\0';
1431 strcat(buffer, urls[i].text);
1432 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1433 end_offset = at_offset + strlen(urls[i].text);
1435 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1436 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1438 /* This assumes no templates start with the URL itself, and that they
1439 have at least two characters before the URL text */
1440 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1441 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1442 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1443 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1444 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1445 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1447 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1448 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1449 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1450 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1451 if (buffer[end_offset] != '\0')
1453 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1454 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1455 if (buffer[end_offset +1] != '\0')
1457 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1458 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1463 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1468 at_pos = strchr(templates_xten_delim[j], 'X');
1469 at_offset = at_pos - templates_xten_delim[j];
1470 strncpy(buffer, templates_xten_delim[j], at_offset);
1471 buffer[at_offset] = '\0';
1472 strcat(buffer, urls[i].text);
1473 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1474 end_offset = at_offset + strlen(urls[i].text);
1476 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1477 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1479 /* This assumes no templates start with the URL itself, and that they
1480 have at least two characters before the URL text */
1481 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1482 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1483 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1484 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1485 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1486 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1490 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1491 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1492 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1493 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1494 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1495 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
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);
1503 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1504 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1506 if (buffer[end_offset +1] != '\0')
1508 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1509 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1510 if (buffer[end_offset +2] != '\0')
1512 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1513 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1518 DestroyWindow(hwndRichEdit);
1519 hwndRichEdit = NULL;
1524 keybd_event('\r', 0x1c, 0, 0); \
1525 keybd_event('\r', 0x1c, KEYEVENTF_KEYUP, 0); \
1526 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
1527 TranslateMessage(&msg); \
1528 DispatchMessage(&msg); \
1534 keybd_event(0x08, 0x0e, 0, 0); \
1535 keybd_event(0x08, 0x0e, KEYEVENTF_KEYUP, 0); \
1536 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
1537 TranslateMessage(&msg); \
1538 DispatchMessage(&msg); \
1542 /* Test detection of URLs within normal text - WM_CHAR case. */
1543 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1544 hwndRichEdit = new_richedit(parent);
1546 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1552 at_pos = strchr(templates_delim[j], 'X');
1553 at_offset = at_pos - templates_delim[j];
1554 end_offset = at_offset + strlen(urls[i].text);
1556 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1557 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1558 for (u = 0; templates_delim[j][u]; u++) {
1559 if (templates_delim[j][u] == '\r') {
1561 } else if (templates_delim[j][u] != 'X') {
1562 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1564 for (v = 0; urls[i].text[v]; v++) {
1565 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1569 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1570 trace("Using template: %s\n", templates_delim[j]);
1572 /* This assumes no templates start with the URL itself, and that they
1573 have at least two characters before the URL text */
1574 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1575 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1576 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1577 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1578 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1579 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1583 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1584 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1585 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1586 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1590 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1591 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1592 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1593 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1595 if (buffer[end_offset] != '\0')
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);
1599 if (buffer[end_offset +1] != '\0')
1601 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1602 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1606 /* The following will insert a paragraph break after the first character
1607 of the URL candidate, thus breaking the URL. It is expected that the
1608 CFE_LINK attribute should break across both pieces of the URL */
1609 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1611 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1613 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1614 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1615 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1616 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1617 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1618 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1620 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1621 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1622 /* end_offset moved because of paragraph break */
1623 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1624 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1625 if (buffer[end_offset+1] != '\0')
1627 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1628 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1629 if (buffer[end_offset +2] != '\0')
1631 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1632 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1636 /* The following will remove the just-inserted paragraph break, thus
1637 restoring the URL */
1638 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1640 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1642 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1643 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1644 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1645 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1646 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1647 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1651 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1652 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1653 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1654 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1658 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1659 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1660 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1661 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1663 if (buffer[end_offset] != '\0')
1665 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1666 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1667 if (buffer[end_offset +1] != '\0')
1669 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1670 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1674 DestroyWindow(hwndRichEdit);
1675 hwndRichEdit = NULL;
1678 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1679 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1682 hwndRichEdit = new_richedit(parent);
1684 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1686 1) Set entire text, a la WM_SETTEXT
1687 2) Set a selection of the text to the URL
1688 3) Set a portion of the text at a time, which eventually results in
1690 All of them should give equivalent results
1693 /* Set entire text in one go, like WM_SETTEXT */
1694 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1699 st.codepage = CP_ACP;
1700 st.flags = ST_DEFAULT;
1702 at_pos = strchr(templates_delim[j], 'X');
1703 at_offset = at_pos - templates_delim[j];
1704 strncpy(buffer, templates_delim[j], at_offset);
1705 buffer[at_offset] = '\0';
1706 strcat(buffer, urls[i].text);
1707 strcat(buffer, templates_delim[j] + at_offset + 1);
1708 end_offset = at_offset + strlen(urls[i].text);
1710 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1711 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1713 /* This assumes no templates start with the URL itself, and that they
1714 have at least two characters before the URL text */
1715 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1716 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1717 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1718 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1719 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1720 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1724 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1725 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1726 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1727 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1731 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1732 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1733 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1734 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1736 if (buffer[end_offset] != '\0')
1738 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1739 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1740 if (buffer[end_offset +1] != '\0')
1742 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1743 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1748 /* Set selection with X to the URL */
1749 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1754 at_pos = strchr(templates_delim[j], 'X');
1755 at_offset = at_pos - templates_delim[j];
1756 end_offset = at_offset + strlen(urls[i].text);
1758 st.codepage = CP_ACP;
1759 st.flags = ST_DEFAULT;
1760 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1761 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1762 st.flags = ST_SELECTION;
1763 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1764 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
1765 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1767 /* This assumes no templates start with the URL itself, and that they
1768 have at least two characters before the URL text */
1769 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1770 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1771 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1772 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1773 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1774 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1778 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1779 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1780 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1781 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1785 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1786 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1787 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1788 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1790 if (buffer[end_offset] != '\0')
1792 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1793 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1794 if (buffer[end_offset +1] != '\0')
1796 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1797 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1802 /* Set selection with X to the first character of the URL, then the rest */
1803 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1808 at_pos = strchr(templates_delim[j], 'X');
1809 at_offset = at_pos - templates_delim[j];
1810 end_offset = at_offset + strlen(urls[i].text);
1812 strcpy(buffer, "YY");
1813 buffer[0] = urls[i].text[0];
1815 st.codepage = CP_ACP;
1816 st.flags = ST_DEFAULT;
1817 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1818 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
1819 st.flags = ST_SELECTION;
1820 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1821 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1822 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1823 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
1824 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1826 /* This assumes no templates start with the URL itself, and that they
1827 have at least two characters before the URL text */
1828 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1829 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1830 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1831 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1832 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1833 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1837 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1838 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1839 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1840 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1844 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1845 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1846 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1847 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1849 if (buffer[end_offset] != '\0')
1851 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1852 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1853 if (buffer[end_offset +1] != '\0')
1855 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1856 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1861 DestroyWindow(hwndRichEdit);
1862 hwndRichEdit = NULL;
1865 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
1866 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1867 hwndRichEdit = new_richedit(parent);
1869 /* Set selection with X to the URL */
1870 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1875 at_pos = strchr(templates_delim[j], 'X');
1876 at_offset = at_pos - templates_delim[j];
1877 end_offset = at_offset + strlen(urls[i].text);
1879 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1880 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
1881 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1882 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
1883 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1885 /* This assumes no templates start with the URL itself, and that they
1886 have at least two characters before the URL text */
1887 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1888 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1889 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1890 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1891 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1892 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1896 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1897 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1898 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1899 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1903 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1904 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1905 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1906 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1908 if (buffer[end_offset] != '\0')
1910 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1911 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1912 if (buffer[end_offset +1] != '\0')
1914 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1915 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1920 /* Set selection with X to the first character of the URL, then the rest */
1921 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1926 at_pos = strchr(templates_delim[j], 'X');
1927 at_offset = at_pos - templates_delim[j];
1928 end_offset = at_offset + strlen(urls[i].text);
1930 strcpy(buffer, "YY");
1931 buffer[0] = urls[i].text[0];
1933 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1934 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
1935 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
1936 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
1937 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
1938 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
1939 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1941 /* This assumes no templates start with the URL itself, and that they
1942 have at least two characters before the URL text */
1943 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1944 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1945 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1946 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1947 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1948 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1952 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1953 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1954 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1955 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1959 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1960 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1961 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1962 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1964 if (buffer[end_offset] != '\0')
1966 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1967 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1968 if (buffer[end_offset +1] != '\0')
1970 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1971 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1976 DestroyWindow(hwndRichEdit);
1977 hwndRichEdit = NULL;
1980 DestroyWindow(parent);
1983 static void test_EM_SCROLL(void)
1986 int r; /* return value */
1987 int expr; /* expected return value */
1988 HWND hwndRichEdit = new_richedit(NULL);
1989 int y_before, y_after; /* units of lines of text */
1992 /* Empty richedit should have scroll range of 0 */
1993 si.cbSize = sizeof(si);
1994 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
1995 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
1996 ok(si.nMin == 0, "si.nMin == %d, expected 0\n", si.nMin);
1997 ok(si.nMax == 0, "si.nMax == %d, expected 0\n", si.nMax);
1998 ok(si.nPos == 0, "si.nPos == %d, expected 0\n", si.nPos);
1999 ok(si.nPage == 0, "si.nPage == %d, expected 0\n", si.nPage);
2001 /* test a richedit box containing a single line of text */
2002 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2004 for (i = 0; i < 4; i++) {
2005 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2007 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2008 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2009 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2010 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2011 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2012 "(i == %d)\n", y_after, i);
2016 * test a richedit box that will scroll. There are two general
2017 * cases: the case without any long lines and the case with a long
2020 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2022 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2024 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2025 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2026 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2027 "LONG LINE \nb\nc\nd\ne");
2028 for (j = 0; j < 12; j++) /* reset scroll position to top */
2029 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2031 /* get first visible line */
2032 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2033 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2035 /* get new current first visible line */
2036 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2038 ok(((r & 0xffffff00) == 0x00010000) &&
2039 ((r & 0x000000ff) != 0x00000000),
2040 "EM_SCROLL page down didn't scroll by a small positive number of "
2041 "lines (r == 0x%08x)\n", r);
2042 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2043 "(line %d scrolled to line %d\n", y_before, y_after);
2047 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2048 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2049 ok(((r & 0xffffff00) == 0x0001ff00),
2050 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2051 "(r == 0x%08x)\n", r);
2052 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2053 "%d scrolled to line %d\n", y_before, y_after);
2057 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2059 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2061 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2062 "(r == 0x%08x)\n", r);
2063 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2064 "1 line (%d scrolled to %d)\n", y_before, y_after);
2068 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2070 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2072 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2073 "(r == 0x%08x)\n", r);
2074 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2075 "line (%d scrolled to %d)\n", y_before, y_after);
2079 r = SendMessage(hwndRichEdit, EM_SCROLL,
2080 SB_LINEUP, 0); /* lineup beyond top */
2082 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2085 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2086 ok(y_before == y_after,
2087 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2091 r = SendMessage(hwndRichEdit, EM_SCROLL,
2092 SB_PAGEUP, 0);/*page up beyond top */
2094 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2097 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2098 ok(y_before == y_after,
2099 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2101 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2102 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2103 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2104 r = SendMessage(hwndRichEdit, EM_SCROLL,
2105 SB_PAGEDOWN, 0); /* page down beyond bot */
2106 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2109 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2110 ok(y_before == y_after,
2111 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2114 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2115 SendMessage(hwndRichEdit, EM_SCROLL,
2116 SB_LINEDOWN, 0); /* line down beyond bot */
2117 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2120 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2121 ok(y_before == y_after,
2122 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2125 DestroyWindow(hwndRichEdit);
2128 static void test_EM_SETUNDOLIMIT(void)
2130 /* cases we test for:
2131 * default behaviour - limiting at 100 undo's
2132 * undo disabled - setting a limit of 0
2133 * undo limited - undo limit set to some to some number, like 2
2134 * bad input - sending a negative number should default to 100 undo's */
2136 HWND hwndRichEdit = new_richedit(NULL);
2141 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2144 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2145 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
2146 also, multiple pastes don't combine like WM_CHAR would */
2147 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2149 /* first case - check the default */
2150 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2151 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
2152 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2153 for (i=0; i<100; i++) /* Undo 100 of them */
2154 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2155 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2156 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
2158 /* second case - cannot undo */
2159 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2160 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
2161 SendMessage(hwndRichEdit,
2162 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
2163 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2164 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
2166 /* third case - set it to an arbitrary number */
2167 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
2168 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
2169 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2170 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2171 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2172 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
2173 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
2174 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
2175 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2176 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2177 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
2178 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2179 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
2180 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
2182 /* fourth case - setting negative numbers should default to 100 undos */
2183 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
2184 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
2186 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
2188 DestroyWindow(hwndRichEdit);
2191 static void test_ES_PASSWORD(void)
2193 /* This isn't hugely testable, so we're just going to run it through its paces */
2195 HWND hwndRichEdit = new_richedit(NULL);
2198 /* First, check the default of a regular control */
2199 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2201 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
2203 /* Now, set it to something normal */
2204 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
2205 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2207 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2209 /* Now, set it to something odd */
2210 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
2211 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
2213 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
2214 DestroyWindow(hwndRichEdit);
2217 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
2222 char** str = (char**)dwCookie;
2225 memcpy(*str, pbBuff, *pcb);
2231 static void test_WM_SETTEXT()
2233 HWND hwndRichEdit = new_richedit(NULL);
2234 const char * TestItem1 = "TestSomeText";
2235 const char * TestItem2 = "TestSomeText\r";
2236 const char * TestItem2_after = "TestSomeText\r\n";
2237 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
2238 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
2239 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
2240 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
2241 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
2242 const char * TestItem5_after = "TestSomeText TestSomeText";
2243 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
2244 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
2245 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
2246 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
2248 char buf[1024] = {0};
2253 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
2254 any solitary \r to be converted to \r\n on return. Properly paired
2255 \r\n are not affected. It also shows that the special sequence \r\r\n
2256 gets converted to a single space.
2259 #define TEST_SETTEXT(a, b) \
2260 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
2261 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
2262 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
2263 ok (result == lstrlen(buf), \
2264 "WM_GETTEXT returned %ld instead of expected %u\n", \
2265 result, lstrlen(buf)); \
2266 result = strcmp(b, buf); \
2268 "WM_SETTEXT round trip: strcmp = %ld\n", result);
2270 TEST_SETTEXT(TestItem1, TestItem1)
2271 TEST_SETTEXT(TestItem2, TestItem2_after)
2272 TEST_SETTEXT(TestItem3, TestItem3_after)
2273 TEST_SETTEXT(TestItem3_after, TestItem3_after)
2274 TEST_SETTEXT(TestItem4, TestItem4_after)
2275 TEST_SETTEXT(TestItem5, TestItem5_after)
2276 TEST_SETTEXT(TestItem6, TestItem6_after)
2277 TEST_SETTEXT(TestItem7, TestItem7_after)
2279 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
2280 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2282 es.dwCookie = (DWORD_PTR)&p;
2284 es.pfnCallback = test_WM_SETTEXT_esCallback;
2285 memset(buf, 0, sizeof(buf));
2286 SendMessage(hwndRichEdit, EM_STREAMOUT,
2287 (WPARAM)(SF_RTF), (LPARAM)&es);
2288 trace("EM_STREAMOUT produced: \n%s\n", buf);
2289 TEST_SETTEXT(buf, TestItem1)
2292 DestroyWindow(hwndRichEdit);
2295 static void test_EM_STREAMOUT(void)
2297 HWND hwndRichEdit = new_richedit(NULL);
2300 char buf[1024] = {0};
2303 const char * TestItem1 = "TestSomeText";
2304 const char * TestItem2 = "TestSomeText\r";
2305 const char * TestItem3 = "TestSomeText\r\n";
2307 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
2309 es.dwCookie = (DWORD_PTR)&p;
2311 es.pfnCallback = test_WM_SETTEXT_esCallback;
2312 memset(buf, 0, sizeof(buf));
2313 SendMessage(hwndRichEdit, EM_STREAMOUT,
2314 (WPARAM)(SF_TEXT), (LPARAM)&es);
2316 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
2317 ok(strcmp(buf, TestItem1) == 0,
2318 "streamed text different, got %s\n", buf);
2320 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
2322 es.dwCookie = (DWORD_PTR)&p;
2324 es.pfnCallback = test_WM_SETTEXT_esCallback;
2325 memset(buf, 0, sizeof(buf));
2326 SendMessage(hwndRichEdit, EM_STREAMOUT,
2327 (WPARAM)(SF_TEXT), (LPARAM)&es);
2329 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
2330 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2331 ok(strcmp(buf, TestItem3) == 0,
2332 "streamed text different from, got %s\n", buf);
2333 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
2335 es.dwCookie = (DWORD_PTR)&p;
2337 es.pfnCallback = test_WM_SETTEXT_esCallback;
2338 memset(buf, 0, sizeof(buf));
2339 SendMessage(hwndRichEdit, EM_STREAMOUT,
2340 (WPARAM)(SF_TEXT), (LPARAM)&es);
2342 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
2343 ok(strcmp(buf, TestItem3) == 0,
2344 "streamed text different, got %s\n", buf);
2346 DestroyWindow(hwndRichEdit);
2349 static void test_EM_SETTEXTEX(void)
2351 HWND hwndRichEdit = new_richedit(NULL);
2354 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2356 'T', 'e', 'x', 't', 0};
2357 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2361 const char * TestItem2_after = "TestSomeText\r\n";
2362 WCHAR TestItem3[] = {'T', 'e', 's', 't',
2365 '\r','\n','\r','\n', 0};
2366 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
2370 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
2374 WCHAR TestItem4[] = {'T', 'e', 's', 't',
2377 '\r','\r','\n','\r',
2379 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
2383 #define MAX_BUF_LEN 1024
2384 WCHAR buf[MAX_BUF_LEN];
2390 setText.codepage = 1200; /* no constant for unicode */
2391 getText.codepage = 1200; /* no constant for unicode */
2392 getText.cb = MAX_BUF_LEN;
2393 getText.flags = GT_DEFAULT;
2394 getText.lpDefaultChar = NULL;
2395 getText.lpUsedDefChar = NULL;
2398 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2399 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2400 ok(lstrcmpW(buf, TestItem1) == 0,
2401 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2403 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
2404 convert \r to \r\n on return
2406 setText.codepage = 1200; /* no constant for unicode */
2407 getText.codepage = 1200; /* no constant for unicode */
2408 getText.cb = MAX_BUF_LEN;
2409 getText.flags = GT_DEFAULT;
2410 getText.lpDefaultChar = NULL;
2411 getText.lpUsedDefChar = NULL;
2413 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
2414 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2415 ok(lstrcmpW(buf, TestItem2) == 0,
2416 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2418 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
2419 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
2420 ok(strcmp((const char *)buf, TestItem2_after) == 0,
2421 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
2423 /* Baseline test for just-enough buffer space for string */
2424 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2425 getText.codepage = 1200; /* no constant for unicode */
2426 getText.flags = GT_DEFAULT;
2427 getText.lpDefaultChar = NULL;
2428 getText.lpUsedDefChar = NULL;
2429 memset(buf, 0, MAX_BUF_LEN);
2430 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2431 ok(lstrcmpW(buf, TestItem2) == 0,
2432 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2434 /* When there is enough space for one character, but not both, of the CRLF
2435 pair at the end of the string, the CR is not copied at all. That is,
2436 the caller must not see CRLF pairs truncated to CR at the end of the
2439 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
2440 getText.codepage = 1200; /* no constant for unicode */
2441 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
2442 getText.lpDefaultChar = NULL;
2443 getText.lpUsedDefChar = NULL;
2444 memset(buf, 0, MAX_BUF_LEN);
2445 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2446 ok(lstrcmpW(buf, TestItem1) == 0,
2447 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2450 /* \r\n pairs get changed into \r */
2451 setText.codepage = 1200; /* no constant for unicode */
2452 getText.codepage = 1200; /* no constant for unicode */
2453 getText.cb = MAX_BUF_LEN;
2454 getText.flags = GT_DEFAULT;
2455 getText.lpDefaultChar = NULL;
2456 getText.lpUsedDefChar = NULL;
2458 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
2459 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2460 ok(lstrcmpW(buf, TestItem3_after) == 0,
2461 "EM_SETTEXTEX did not convert properly\n");
2463 /* \n also gets changed to \r */
2464 setText.codepage = 1200; /* no constant for unicode */
2465 getText.codepage = 1200; /* no constant for unicode */
2466 getText.cb = MAX_BUF_LEN;
2467 getText.flags = GT_DEFAULT;
2468 getText.lpDefaultChar = NULL;
2469 getText.lpUsedDefChar = NULL;
2471 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
2472 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2473 ok(lstrcmpW(buf, TestItem3_after) == 0,
2474 "EM_SETTEXTEX did not convert properly\n");
2476 /* \r\r\n gets changed into single space */
2477 setText.codepage = 1200; /* no constant for unicode */
2478 getText.codepage = 1200; /* no constant for unicode */
2479 getText.cb = MAX_BUF_LEN;
2480 getText.flags = GT_DEFAULT;
2481 getText.lpDefaultChar = NULL;
2482 getText.lpUsedDefChar = NULL;
2484 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
2485 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2486 ok(lstrcmpW(buf, TestItem4_after) == 0,
2487 "EM_SETTEXTEX did not convert properly\n");
2489 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2490 (WPARAM)&setText, (LPARAM) NULL);
2491 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2494 "EM_SETTEXTEX returned %d, instead of 1\n",result);
2495 ok(lstrlenW(buf) == 0,
2496 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
2498 /* put some text back */
2500 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2501 /* select some text */
2504 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2505 /* replace current selection */
2506 setText.flags = ST_SELECTION;
2507 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2508 (WPARAM)&setText, (LPARAM) NULL);
2510 "EM_SETTEXTEX with NULL lParam to replace selection"
2511 " with no text should return 0. Got %i\n",
2514 /* put some text back */
2516 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
2517 /* select some text */
2520 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
2521 /* replace current selection */
2522 setText.flags = ST_SELECTION;
2523 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
2524 (WPARAM)&setText, (LPARAM) TestItem1);
2526 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2527 ok(result == lstrlenW(TestItem1),
2528 "EM_SETTEXTEX with NULL lParam to replace selection"
2529 " with no text should return 0. Got %i\n",
2531 ok(lstrlenW(buf) == 22,
2532 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
2535 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
2536 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
2538 es.dwCookie = (DWORD_PTR)&p;
2540 es.pfnCallback = test_WM_SETTEXT_esCallback;
2541 memset(buf, 0, sizeof(buf));
2542 SendMessage(hwndRichEdit, EM_STREAMOUT,
2543 (WPARAM)(SF_RTF), (LPARAM)&es);
2544 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
2546 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
2547 getText.codepage = 1200; /* no constant for unicode */
2548 getText.cb = MAX_BUF_LEN;
2549 getText.flags = GT_DEFAULT;
2550 getText.lpDefaultChar = NULL;
2551 getText.lpUsedDefChar = NULL;
2554 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
2555 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
2556 ok(lstrcmpW(buf, TestItem1) == 0,
2557 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
2560 DestroyWindow(hwndRichEdit);
2563 static void test_EM_LIMITTEXT(void)
2567 HWND hwndRichEdit = new_richedit(NULL);
2569 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
2570 * about setting the length to -1 for multiline edit controls doesn't happen.
2573 /* Don't check default gettextlimit case. That's done in other tests */
2575 /* Set textlimit to 100 */
2576 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
2577 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2579 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
2581 /* Set textlimit to 0 */
2582 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
2583 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2585 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
2587 /* Set textlimit to -1 */
2588 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
2589 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2591 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
2593 /* Set textlimit to -2 */
2594 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
2595 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2597 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
2599 DestroyWindow (hwndRichEdit);
2603 static void test_EM_EXLIMITTEXT(void)
2605 int i, selBegin, selEnd, len1, len2;
2607 char text[1024 + 1];
2608 char buffer[1024 + 1];
2609 int textlimit = 0; /* multiple of 100 */
2610 HWND hwndRichEdit = new_richedit(NULL);
2612 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2613 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
2616 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2617 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2619 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2622 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2623 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2625 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
2627 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
2628 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2629 /* default for WParam = 0 */
2630 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
2632 textlimit = sizeof(text)-1;
2633 memset(text, 'W', textlimit);
2634 text[sizeof(text)-1] = 0;
2635 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2636 /* maxed out text */
2637 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2639 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2640 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2641 len1 = selEnd - selBegin;
2643 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
2644 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
2645 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
2646 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2647 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2648 len2 = selEnd - selBegin;
2651 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2654 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2655 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2656 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
2657 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2658 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2659 len1 = selEnd - selBegin;
2662 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2665 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
2666 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2667 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
2668 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2669 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
2670 len2 = selEnd - selBegin;
2673 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
2676 /* set text up to the limit, select all the text, then add a char */
2678 memset(text, 'W', textlimit);
2679 text[textlimit] = 0;
2680 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2681 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2682 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
2683 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
2684 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2685 result = strcmp(buffer, "A");
2686 ok(0 == result, "got string = \"%s\"\n", buffer);
2688 /* WM_SETTEXT not limited */
2690 memset(text, 'W', textlimit);
2691 text[textlimit] = 0;
2692 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
2693 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
2694 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2696 ok(10 == i, "expected 10 chars\n");
2697 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2698 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2700 /* try inserting more text at end */
2701 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2702 ok(0 == i, "WM_CHAR wasn't processed\n");
2703 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2705 ok(10 == i, "expected 10 chars, got %i\n", i);
2706 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2707 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2709 /* try inserting text at beginning */
2710 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
2711 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2712 ok(0 == i, "WM_CHAR wasn't processed\n");
2713 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2715 ok(10 == i, "expected 10 chars, got %i\n", i);
2716 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2717 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
2719 /* WM_CHAR is limited */
2721 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
2722 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
2723 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2724 ok(0 == i, "WM_CHAR wasn't processed\n");
2725 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2726 ok(0 == i, "WM_CHAR wasn't processed\n");
2727 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
2729 ok(1 == i, "expected 1 chars, got %i instead\n", i);
2731 DestroyWindow(hwndRichEdit);
2734 static void test_EM_GETLIMITTEXT(void)
2737 HWND hwndRichEdit = new_richedit(NULL);
2739 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2740 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
2742 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
2743 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
2744 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
2746 DestroyWindow(hwndRichEdit);
2749 static void test_WM_SETFONT(void)
2751 /* There is no invalid input or error conditions for this function.
2752 * NULL wParam and lParam just fall back to their default values
2753 * It should be noted that even if you use a gibberish name for your fonts
2754 * here, it will still work because the name is stored. They will display as
2755 * System, but will report their name to be whatever they were created as */
2757 HWND hwndRichEdit = new_richedit(NULL);
2758 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2759 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2760 FF_DONTCARE, "Marlett");
2761 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2762 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2763 FF_DONTCARE, "MS Sans Serif");
2764 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
2765 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
2766 FF_DONTCARE, "Courier");
2767 LOGFONTA sentLogFont;
2768 CHARFORMAT2A returnedCF2A;
2770 returnedCF2A.cbSize = sizeof(returnedCF2A);
2772 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
2773 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
2774 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2776 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
2777 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2778 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
2779 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2781 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
2782 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2783 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
2784 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2785 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
2786 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2788 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
2789 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2790 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
2791 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
2792 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
2793 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
2795 /* This last test is special since we send in NULL. We clear the variables
2796 * and just compare to "System" instead of the sent in font name. */
2797 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
2798 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
2799 returnedCF2A.cbSize = sizeof(returnedCF2A);
2801 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
2802 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
2803 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
2804 ok (!strcmp("System",returnedCF2A.szFaceName),
2805 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
2807 DestroyWindow(hwndRichEdit);
2811 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
2816 const char** str = (const char**)dwCookie;
2817 int size = strlen(*str);
2818 if(size > 3) /* let's make it piecemeal for fun */
2825 memcpy(pbBuff, *str, *pcb);
2831 static void test_EM_GETMODIFY(void)
2833 HWND hwndRichEdit = new_richedit(NULL);
2836 WCHAR TestItem1[] = {'T', 'e', 's', 't',
2838 'T', 'e', 'x', 't', 0};
2839 WCHAR TestItem2[] = {'T', 'e', 's', 't',
2841 'O', 't', 'h', 'e', 'r',
2842 'T', 'e', 'x', 't', 0};
2843 const char* streamText = "hello world";
2848 HFONT testFont = 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");
2852 setText.codepage = 1200; /* no constant for unicode */
2853 setText.flags = ST_KEEPUNDO;
2856 /* modify flag shouldn't be set when richedit is first created */
2857 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2859 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
2861 /* setting modify flag should actually set it */
2862 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
2863 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2865 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
2867 /* clearing modify flag should actually clear it */
2868 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2869 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2871 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
2873 /* setting font doesn't change modify flag */
2874 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2875 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
2876 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2878 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
2880 /* setting text should set modify flag */
2881 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2882 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2883 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2885 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
2887 /* undo previous text doesn't reset modify flag */
2888 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
2889 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2891 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
2893 /* set text with no flag to keep undo stack should not set modify flag */
2894 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2896 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2897 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2899 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
2901 /* WM_SETTEXT doesn't modify */
2902 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2903 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
2904 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2906 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
2908 /* clear the text */
2909 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2910 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
2911 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2913 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
2916 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2917 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
2918 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2919 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
2920 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2922 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
2924 /* copy/paste text 1 */
2925 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2926 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2927 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2928 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2929 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2931 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
2933 /* copy/paste text 2 */
2934 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2935 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
2936 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
2937 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
2938 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
2939 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2941 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
2944 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2945 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
2946 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2947 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2949 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
2952 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
2953 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2954 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
2955 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2957 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
2959 /* set char format */
2960 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2961 cf2.cbSize = sizeof(CHARFORMAT2);
2962 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
2964 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
2965 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
2966 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
2967 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
2968 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
2969 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2971 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
2973 /* set para format */
2974 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2975 pf2.cbSize = sizeof(PARAFORMAT2);
2976 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
2978 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
2979 pf2.wAlignment = PFA_RIGHT;
2980 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
2981 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2983 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
2986 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
2987 es.dwCookie = (DWORD_PTR)&streamText;
2989 es.pfnCallback = test_EM_GETMODIFY_esCallback;
2990 SendMessage(hwndRichEdit, EM_STREAMIN,
2991 (WPARAM)(SF_TEXT), (LPARAM)&es);
2992 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
2994 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
2996 DestroyWindow(hwndRichEdit);
3002 long expected_retval;
3003 int expected_getsel_start;
3004 int expected_getsel_end;
3005 int _exsetsel_todo_wine;
3006 int _getsel_todo_wine;
3009 const struct exsetsel_s exsetsel_tests[] = {
3011 {5, 10, 10, 5, 10, 0, 0},
3012 {15, 17, 17, 15, 17, 0, 0},
3013 /* test cpMax > strlen() */
3014 {0, 100, 18, 0, 18, 0, 1},
3015 /* test cpMin == cpMax */
3016 {5, 5, 5, 5, 5, 0, 0},
3017 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
3018 {-1, 0, 5, 5, 5, 0, 0},
3019 {-1, 17, 5, 5, 5, 0, 0},
3020 {-1, 18, 5, 5, 5, 0, 0},
3021 /* test cpMin < 0 && cpMax < 0 */
3022 {-1, -1, 17, 17, 17, 0, 0},
3023 {-4, -5, 17, 17, 17, 0, 0},
3024 /* test cMin >=0 && cpMax < 0 (bug 6814) */
3025 {0, -1, 18, 0, 18, 0, 1},
3026 {17, -5, 18, 17, 18, 0, 1},
3027 {18, -3, 17, 17, 17, 0, 0},
3028 /* test if cpMin > cpMax */
3029 {15, 19, 18, 15, 18, 0, 1},
3030 {19, 15, 18, 15, 18, 0, 1}
3033 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
3038 cr.cpMin = setsel->min;
3039 cr.cpMax = setsel->max;
3040 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
3042 if (setsel->_exsetsel_todo_wine) {
3044 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3047 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
3050 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
3052 if (setsel->_getsel_todo_wine) {
3054 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);
3057 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);
3061 static void test_EM_EXSETSEL(void)
3063 HWND hwndRichEdit = new_richedit(NULL);
3065 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
3067 /* sending some text to the window */
3068 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3069 /* 01234567890123456*/
3072 for (i = 0; i < num_tests; i++) {
3073 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
3076 DestroyWindow(hwndRichEdit);
3079 static void test_EM_REPLACESEL(int redraw)
3081 HWND hwndRichEdit = new_richedit(NULL);
3082 char buffer[1024] = {0};
3087 /* sending some text to the window */
3088 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
3089 /* 01234567890123456*/
3092 /* FIXME add more tests */
3093 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
3094 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
3095 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
3096 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3097 r = strcmp(buffer, "testing");
3098 ok(0 == r, "expected %d, got %d\n", 0, r);
3100 DestroyWindow(hwndRichEdit);
3102 hwndRichEdit = new_richedit(NULL);
3104 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
3105 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
3107 /* Test behavior with carriage returns and newlines */
3108 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3109 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
3110 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
3111 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3112 r = strcmp(buffer, "RichEdit1");
3113 ok(0 == r, "expected %d, got %d\n", 0, r);
3115 getText.codepage = CP_ACP;
3116 getText.flags = GT_DEFAULT;
3117 getText.lpDefaultChar = NULL;
3118 getText.lpUsedDefChar = NULL;
3119 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3120 ok(strcmp(buffer, "RichEdit1") == 0,
3121 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
3123 /* Test number of lines reported after EM_REPLACESEL */
3124 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3125 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3127 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3128 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
3129 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
3130 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3131 r = strcmp(buffer, "RichEdit1\r\n");
3132 ok(0 == r, "expected %d, got %d\n", 0, r);
3134 getText.codepage = CP_ACP;
3135 getText.flags = GT_DEFAULT;
3136 getText.lpDefaultChar = NULL;
3137 getText.lpUsedDefChar = NULL;
3138 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3139 ok(strcmp(buffer, "RichEdit1\r") == 0,
3140 "EM_GETTEXTEX returned incorrect string\n");
3142 /* Test number of lines reported after EM_REPLACESEL */
3143 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3144 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3146 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
3147 EM_REPLACESEL. The general rule seems to be that Win98's riched20
3148 returns the number of characters *inserted* into the control (after
3149 required conversions), but WinXP's riched20 returns the number of
3150 characters interpreted from the original lParam. Wine's builtin riched20
3151 implements the WinXP behavior.
3153 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3154 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
3155 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
3156 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
3158 /* Test number of lines reported after EM_REPLACESEL */
3159 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3160 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3162 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3163 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3164 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3165 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3167 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3168 r = strcmp(buffer, "RichEdit1\r\n");
3169 ok(0 == r, "expected %d, got %d\n", 0, r);
3171 getText.codepage = CP_ACP;
3172 getText.flags = GT_DEFAULT;
3173 getText.lpDefaultChar = NULL;
3174 getText.lpUsedDefChar = NULL;
3175 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3176 ok(strcmp(buffer, "RichEdit1\r") == 0,
3177 "EM_GETTEXTEX returned incorrect string\n");
3179 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3180 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3181 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
3182 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
3184 /* The following tests show that richedit should handle the special \r\r\n
3185 sequence by turning it into a single space on insertion. However,
3186 EM_REPLACESEL on WinXP returns the number of characters in the original
3190 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3191 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
3192 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
3193 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3194 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3195 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3196 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3198 /* Test the actual string */
3200 getText.codepage = CP_ACP;
3201 getText.flags = GT_DEFAULT;
3202 getText.lpDefaultChar = NULL;
3203 getText.lpUsedDefChar = NULL;
3204 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3205 ok(strcmp(buffer, "\r\r") == 0,
3206 "EM_GETTEXTEX returned incorrect string\n");
3208 /* Test number of lines reported after EM_REPLACESEL */
3209 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3210 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3212 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3213 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
3214 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
3215 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
3216 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3217 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3218 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
3219 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
3221 /* Test the actual string */
3223 getText.codepage = CP_ACP;
3224 getText.flags = GT_DEFAULT;
3225 getText.lpDefaultChar = NULL;
3226 getText.lpUsedDefChar = NULL;
3227 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3228 ok(strcmp(buffer, " ") == 0,
3229 "EM_GETTEXTEX returned incorrect string\n");
3231 /* Test number of lines reported after EM_REPLACESEL */
3232 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3233 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
3235 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3236 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
3237 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3238 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3239 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3240 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3241 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3242 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3244 /* Test the actual string */
3246 getText.codepage = CP_ACP;
3247 getText.flags = GT_DEFAULT;
3248 getText.lpDefaultChar = NULL;
3249 getText.lpUsedDefChar = NULL;
3250 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3251 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
3252 "EM_GETTEXTEX returned incorrect string\n");
3254 /* Test number of lines reported after EM_REPLACESEL */
3255 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3256 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3258 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3259 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
3260 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
3261 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
3262 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3263 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3264 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3265 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3267 /* Test the actual string */
3269 getText.codepage = CP_ACP;
3270 getText.flags = GT_DEFAULT;
3271 getText.lpDefaultChar = NULL;
3272 getText.lpUsedDefChar = NULL;
3273 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3274 ok(strcmp(buffer, " \r") == 0,
3275 "EM_GETTEXTEX returned incorrect string\n");
3277 /* Test number of lines reported after EM_REPLACESEL */
3278 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3279 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
3281 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3282 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
3283 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
3284 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
3285 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3286 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3287 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
3288 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
3290 /* Test the actual string */
3292 getText.codepage = CP_ACP;
3293 getText.flags = GT_DEFAULT;
3294 getText.lpDefaultChar = NULL;
3295 getText.lpUsedDefChar = NULL;
3296 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3297 ok(strcmp(buffer, " \r\r") == 0,
3298 "EM_GETTEXTEX returned incorrect string\n");
3300 /* Test number of lines reported after EM_REPLACESEL */
3301 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3302 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3304 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3305 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
3306 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
3307 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
3308 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3309 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3310 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
3311 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
3313 /* Test the actual string */
3315 getText.codepage = CP_ACP;
3316 getText.flags = GT_DEFAULT;
3317 getText.lpDefaultChar = NULL;
3318 getText.lpUsedDefChar = NULL;
3319 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3320 ok(strcmp(buffer, "\rX\r\r\r") == 0,
3321 "EM_GETTEXTEX returned incorrect string\n");
3323 /* Test number of lines reported after EM_REPLACESEL */
3324 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3325 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
3327 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3328 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
3329 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
3330 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3331 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3332 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
3333 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
3335 /* Test the actual string */
3337 getText.codepage = CP_ACP;
3338 getText.flags = GT_DEFAULT;
3339 getText.lpDefaultChar = NULL;
3340 getText.lpUsedDefChar = NULL;
3341 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3342 ok(strcmp(buffer, "\r\r") == 0,
3343 "EM_GETTEXTEX returned incorrect string\n");
3345 /* Test number of lines reported after EM_REPLACESEL */
3346 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3347 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
3349 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
3350 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
3351 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
3352 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
3353 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
3354 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
3355 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
3356 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
3358 /* Test the actual string */
3360 getText.codepage = CP_ACP;
3361 getText.flags = GT_DEFAULT;
3362 getText.lpDefaultChar = NULL;
3363 getText.lpUsedDefChar = NULL;
3364 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
3365 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
3366 "EM_GETTEXTEX returned incorrect string\n");
3368 /* Test number of lines reported after EM_REPLACESEL */
3369 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
3370 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
3372 DestroyWindow(hwndRichEdit);
3375 static void test_WM_PASTE(void)
3379 char buffer[1024] = {0};
3380 char key_info[][3] =
3382 /* VirtualKey, ScanCode, WM_CHAR code */
3383 {'C', 0x2e, 3}, /* Ctrl-C */
3384 {'X', 0x2d, 24}, /* Ctrl-X */
3385 {'V', 0x2f, 22}, /* Ctrl-V */
3386 {'Z', 0x2c, 26}, /* Ctrl-Z */
3387 {'Y', 0x15, 25}, /* Ctrl-Y */
3389 const char* text1 = "testing paste\r";
3390 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
3391 const char* text1_after = "testing paste\r\n";
3392 const char* text2 = "testing paste\r\rtesting paste";
3393 const char* text2_after = "testing paste\r\n\r\ntesting paste";
3394 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
3395 HWND hwndRichEdit = new_richedit(NULL);
3397 /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP
3398 messages, probably because it inspects the keyboard state itself.
3399 Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
3401 #define SEND_CTRL_KEY(hwnd, k) \
3402 keybd_event(VK_CONTROL, 0x1d, 0, 0);\
3403 keybd_event(k[0], k[1], 0, 0);\
3404 keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
3405 keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
3406 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
3407 TranslateMessage(&msg); \
3408 DispatchMessage(&msg); \
3411 #define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
3412 #define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
3413 #define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
3414 #define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
3415 #define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
3417 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
3418 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
3420 SEND_CTRL_C(hwndRichEdit) /* Copy */
3421 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3422 SEND_CTRL_V(hwndRichEdit) /* Paste */
3423 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3424 /* Pasted text should be visible at this step */
3425 result = strcmp(text1_step1, buffer);
3427 "test paste: strcmp = %i\n", result);
3428 SEND_CTRL_Z(hwndRichEdit) /* Undo */
3429 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3430 /* Text should be the same as before (except for \r -> \r\n conversion) */
3431 result = strcmp(text1_after, buffer);
3433 "test paste: strcmp = %i\n", result);
3435 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
3436 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
3437 SEND_CTRL_C(hwndRichEdit) /* Copy */
3438 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
3439 SEND_CTRL_V(hwndRichEdit) /* Paste */
3440 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3441 /* Pasted text should be visible at this step */
3442 result = strcmp(text3, buffer);
3444 "test paste: strcmp = %i\n", result);
3445 SEND_CTRL_Z(hwndRichEdit) /* Undo */
3446 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3447 /* Text should be the same as before (except for \r -> \r\n conversion) */
3448 result = strcmp(text2_after, buffer);
3450 "test paste: strcmp = %i\n", result);
3451 SEND_CTRL_Y(hwndRichEdit) /* Redo */
3452 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3453 /* Text should revert to post-paste state */
3454 result = strcmp(buffer,text3);
3456 "test paste: strcmp = %i\n", result);
3458 DestroyWindow(hwndRichEdit);
3461 static void test_EM_FORMATRANGE(void)
3466 HWND hwndRichEdit = new_richedit(NULL);
3468 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
3470 hdc = GetDC(hwndRichEdit);
3471 ok(hdc != NULL, "Could not get HDC\n");
3473 fr.hdc = fr.hdcTarget = hdc;
3474 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
3475 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
3476 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
3480 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3482 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3485 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3487 ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r);
3493 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
3495 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
3498 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
3500 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
3503 DestroyWindow(hwndRichEdit);
3506 static int nCallbackCount = 0;
3508 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
3511 const char text[] = {'t','e','s','t'};
3513 if (sizeof(text) <= cb)
3515 if ((int)dwCookie != nCallbackCount)
3521 memcpy (pbBuff, text, sizeof(text));
3522 *pcb = sizeof(text);
3529 return 1; /* indicates callback failed */
3532 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
3537 const char** str = (const char**)dwCookie;
3538 int size = strlen(*str);
3544 memcpy(pbBuff, *str, *pcb);
3551 static void test_EM_STREAMIN(void)
3553 HWND hwndRichEdit = new_richedit(NULL);
3556 char buffer[1024] = {0};
3558 const char * streamText0 = "{\\rtf1 TestSomeText}";
3559 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
3560 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
3562 const char * streamText1 =
3563 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
3564 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
3567 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
3568 const char * streamText2 =
3569 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
3570 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
3571 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
3572 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
3573 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
3574 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
3575 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
3577 const char * streamText3 = "RichEdit1";
3579 /* Minimal test without \par at the end */
3580 es.dwCookie = (DWORD_PTR)&streamText0;
3582 es.pfnCallback = test_EM_STREAMIN_esCallback;
3583 SendMessage(hwndRichEdit, EM_STREAMIN,
3584 (WPARAM)(SF_RTF), (LPARAM)&es);
3586 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3588 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
3589 result = strcmp (buffer,"TestSomeText");
3591 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
3592 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
3594 /* Native richedit 2.0 ignores last \par */
3595 es.dwCookie = (DWORD_PTR)&streamText0a;
3597 es.pfnCallback = test_EM_STREAMIN_esCallback;
3598 SendMessage(hwndRichEdit, EM_STREAMIN,
3599 (WPARAM)(SF_RTF), (LPARAM)&es);
3601 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3603 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
3604 result = strcmp (buffer,"TestSomeText");
3606 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
3607 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
3609 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
3610 es.dwCookie = (DWORD_PTR)&streamText0b;
3612 es.pfnCallback = test_EM_STREAMIN_esCallback;
3613 SendMessage(hwndRichEdit, EM_STREAMIN,
3614 (WPARAM)(SF_RTF), (LPARAM)&es);
3616 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3618 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
3619 result = strcmp (buffer,"TestSomeText\r\n");
3621 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
3622 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
3624 es.dwCookie = (DWORD_PTR)&streamText1;
3626 es.pfnCallback = test_EM_STREAMIN_esCallback;
3627 SendMessage(hwndRichEdit, EM_STREAMIN,
3628 (WPARAM)(SF_RTF), (LPARAM)&es);
3630 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3632 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
3633 result = strcmp (buffer,"TestSomeText");
3635 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3636 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
3638 es.dwCookie = (DWORD_PTR)&streamText2;
3640 SendMessage(hwndRichEdit, EM_STREAMIN,
3641 (WPARAM)(SF_RTF), (LPARAM)&es);
3643 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3645 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
3646 ok (strlen(buffer) == 0,
3647 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3648 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
3650 es.dwCookie = (DWORD_PTR)&streamText3;
3652 SendMessage(hwndRichEdit, EM_STREAMIN,
3653 (WPARAM)(SF_RTF), (LPARAM)&es);
3655 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3657 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
3658 ok (strlen(buffer) == 0,
3659 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
3660 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
3662 DestroyWindow(hwndRichEdit);
3665 static void test_EM_StreamIn_Undo(void)
3667 /* The purpose of this test is to determine when a EM_StreamIn should be
3668 * undoable. This is important because WM_PASTE currently uses StreamIn and
3669 * pasting should always be undoable but streaming isn't always.
3672 * StreamIn plain text without SFF_SELECTION.
3673 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
3674 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
3675 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
3676 * Feel free to add tests for other text modes or StreamIn things.
3680 HWND hwndRichEdit = new_richedit(NULL);
3683 char buffer[1024] = {0};
3684 const char randomtext[] = "Some text";
3686 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
3688 /* StreamIn, no SFF_SELECTION */
3689 es.dwCookie = nCallbackCount;
3690 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3691 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3692 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3693 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
3694 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3695 result = strcmp (buffer,"test");
3697 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
3699 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3700 ok (result == FALSE,
3701 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
3703 /* StreamIn, SFF_SELECTION, but nothing selected */
3704 es.dwCookie = nCallbackCount;
3705 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3706 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3707 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
3708 SendMessage(hwndRichEdit, EM_STREAMIN,
3709 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3710 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3711 result = strcmp (buffer,"testSome text");
3713 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3715 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3717 "EM_STREAMIN with SFF_SELECTION but no selection set "
3718 "should create an undo\n");
3720 /* StreamIn, SFF_SELECTION, with a selection */
3721 es.dwCookie = nCallbackCount;
3722 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3723 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
3724 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
3725 SendMessage(hwndRichEdit, EM_STREAMIN,
3726 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
3727 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3728 result = strcmp (buffer,"Sometesttext");
3730 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
3732 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
3734 "EM_STREAMIN with SFF_SELECTION and selection set "
3735 "should create an undo\n");
3739 static BOOL is_em_settextex_supported(HWND hwnd)
3741 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
3742 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
3745 static void test_unicode_conversions(void)
3747 static const WCHAR tW[] = {'t',0};
3748 static const WCHAR teW[] = {'t','e',0};
3749 static const WCHAR textW[] = {'t','e','s','t',0};
3750 static const char textA[] = "test";
3754 int is_win9x, em_settextex_supported, ret;
3756 is_win9x = GetVersion() & 0x80000000;
3758 #define set_textA(hwnd, wm_set_text, txt) \
3760 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
3761 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3762 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3763 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3764 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
3766 #define expect_textA(hwnd, wm_get_text, txt) \
3768 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3769 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
3770 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3771 memset(bufA, 0xAA, sizeof(bufA)); \
3772 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3773 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3774 ret = lstrcmpA(bufA, txt); \
3775 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
3778 #define set_textW(hwnd, wm_set_text, txt) \
3780 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
3781 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
3782 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
3783 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
3784 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
3786 #define expect_textW(hwnd, wm_get_text, txt) \
3788 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
3789 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
3790 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3791 memset(bufW, 0xAA, sizeof(bufW)); \
3794 assert(wm_get_text == EM_GETTEXTEX); \
3795 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3796 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
3800 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
3801 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
3803 ret = lstrcmpW(bufW, txt); \
3804 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
3806 #define expect_empty(hwnd, wm_get_text) \
3808 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
3809 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
3810 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
3811 memset(bufA, 0xAA, sizeof(bufA)); \
3812 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
3813 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
3814 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
3817 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3818 0, 0, 200, 60, 0, 0, 0, 0);
3819 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3821 ret = IsWindowUnicode(hwnd);
3823 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
3825 ok(ret, "RichEdit20W should be unicode under NT\n");
3827 /* EM_SETTEXTEX is supported starting from version 3.0 */
3828 em_settextex_supported = is_em_settextex_supported(hwnd);
3829 trace("EM_SETTEXTEX is %ssupported on this platform\n",
3830 em_settextex_supported ? "" : "NOT ");
3832 expect_empty(hwnd, WM_GETTEXT);
3833 expect_empty(hwnd, EM_GETTEXTEX);
3835 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
3836 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3837 expect_textA(hwnd, WM_GETTEXT, "t");
3838 expect_textA(hwnd, EM_GETTEXTEX, "t");
3839 expect_textW(hwnd, EM_GETTEXTEX, tW);
3841 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
3842 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
3843 expect_textA(hwnd, WM_GETTEXT, "te");
3844 expect_textA(hwnd, EM_GETTEXTEX, "te");
3845 expect_textW(hwnd, EM_GETTEXTEX, teW);
3847 set_textA(hwnd, WM_SETTEXT, NULL);
3848 expect_empty(hwnd, WM_GETTEXT);
3849 expect_empty(hwnd, EM_GETTEXTEX);
3852 set_textA(hwnd, WM_SETTEXT, textW);
3854 set_textA(hwnd, WM_SETTEXT, textA);
3855 expect_textA(hwnd, WM_GETTEXT, textA);
3856 expect_textA(hwnd, EM_GETTEXTEX, textA);
3857 expect_textW(hwnd, EM_GETTEXTEX, textW);
3859 if (em_settextex_supported)
3861 set_textA(hwnd, EM_SETTEXTEX, textA);
3862 expect_textA(hwnd, WM_GETTEXT, textA);
3863 expect_textA(hwnd, EM_GETTEXTEX, textA);
3864 expect_textW(hwnd, EM_GETTEXTEX, textW);
3869 set_textW(hwnd, WM_SETTEXT, textW);
3870 expect_textW(hwnd, WM_GETTEXT, textW);
3871 expect_textA(hwnd, WM_GETTEXT, textA);
3872 expect_textW(hwnd, EM_GETTEXTEX, textW);
3873 expect_textA(hwnd, EM_GETTEXTEX, textA);
3875 if (em_settextex_supported)
3877 set_textW(hwnd, EM_SETTEXTEX, textW);
3878 expect_textW(hwnd, WM_GETTEXT, textW);
3879 expect_textA(hwnd, WM_GETTEXT, textA);
3880 expect_textW(hwnd, EM_GETTEXTEX, textW);
3881 expect_textA(hwnd, EM_GETTEXTEX, textA);
3884 DestroyWindow(hwnd);
3886 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
3887 0, 0, 200, 60, 0, 0, 0, 0);
3888 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3890 ret = IsWindowUnicode(hwnd);
3891 ok(!ret, "RichEdit20A should NOT be unicode\n");
3893 set_textA(hwnd, WM_SETTEXT, textA);
3894 expect_textA(hwnd, WM_GETTEXT, textA);
3895 expect_textA(hwnd, EM_GETTEXTEX, textA);
3896 expect_textW(hwnd, EM_GETTEXTEX, textW);
3898 if (em_settextex_supported)
3900 set_textA(hwnd, EM_SETTEXTEX, textA);
3901 expect_textA(hwnd, WM_GETTEXT, textA);
3902 expect_textA(hwnd, EM_GETTEXTEX, textA);
3903 expect_textW(hwnd, EM_GETTEXTEX, textW);
3908 set_textW(hwnd, WM_SETTEXT, textW);
3909 expect_textW(hwnd, WM_GETTEXT, textW);
3910 expect_textA(hwnd, WM_GETTEXT, textA);
3911 expect_textW(hwnd, EM_GETTEXTEX, textW);
3912 expect_textA(hwnd, EM_GETTEXTEX, textA);
3914 if (em_settextex_supported)
3916 set_textW(hwnd, EM_SETTEXTEX, textW);
3917 expect_textW(hwnd, WM_GETTEXT, textW);
3918 expect_textA(hwnd, WM_GETTEXT, textA);
3919 expect_textW(hwnd, EM_GETTEXTEX, textW);
3920 expect_textA(hwnd, EM_GETTEXTEX, textA);
3923 DestroyWindow(hwnd);
3926 static void test_WM_CHAR(void)
3930 const char * char_list = "abc\rabc\r";
3931 const char * expected_content_single = "abcabc";
3932 const char * expected_content_multi = "abc\r\nabc\r\n";
3933 char buffer[64] = {0};
3936 /* single-line control must IGNORE carriage returns */
3937 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3938 0, 0, 200, 60, 0, 0, 0, 0);
3939 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3942 while (*p != '\0') {
3943 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
3944 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
3945 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
3946 SendMessageA(hwnd, WM_KEYUP, *p, 1);
3950 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3951 ret = strcmp(buffer, expected_content_single);
3952 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3954 DestroyWindow(hwnd);
3956 /* multi-line control inserts CR normally */
3957 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
3958 0, 0, 200, 60, 0, 0, 0, 0);
3959 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3962 while (*p != '\0') {
3963 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
3964 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
3965 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
3966 SendMessageA(hwnd, WM_KEYUP, *p, 1);
3970 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
3971 ret = strcmp(buffer, expected_content_multi);
3972 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
3974 DestroyWindow(hwnd);
3977 static void test_EM_GETTEXTLENGTHEX(void)
3980 GETTEXTLENGTHEX gtl;
3982 const char * base_string = "base string";
3983 const char * test_string = "a\nb\n\n\r\n";
3984 const char * test_string_after = "a";
3985 const char * test_string_2 = "a\rtest\rstring";
3986 char buffer[64] = {0};
3989 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
3990 0, 0, 200, 60, 0, 0, 0, 0);
3991 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
3993 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
3994 gtl.codepage = CP_ACP;
3995 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
3996 ok(ret == 0, "ret %d\n",ret);
3998 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
3999 gtl.codepage = CP_ACP;
4000 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4001 ok(ret == 0, "ret %d\n",ret);
4003 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4005 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4006 gtl.codepage = CP_ACP;
4007 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4008 ok(ret == strlen(base_string), "ret %d\n",ret);
4010 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4011 gtl.codepage = CP_ACP;
4012 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4013 ok(ret == strlen(base_string), "ret %d\n",ret);
4015 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4017 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4018 gtl.codepage = CP_ACP;
4019 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4020 ok(ret == 1, "ret %d\n",ret);
4022 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4023 gtl.codepage = CP_ACP;
4024 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4025 ok(ret == 1, "ret %d\n",ret);
4027 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
4028 ret = strcmp(buffer, test_string_after);
4029 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
4031 DestroyWindow(hwnd);
4034 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
4035 0, 0, 200, 60, 0, 0, 0, 0);
4036 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4038 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4039 gtl.codepage = CP_ACP;
4040 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4041 ok(ret == 0, "ret %d\n",ret);
4043 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4044 gtl.codepage = CP_ACP;
4045 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4046 ok(ret == 0, "ret %d\n",ret);
4048 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
4050 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4051 gtl.codepage = CP_ACP;
4052 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4053 ok(ret == strlen(base_string), "ret %d\n",ret);
4055 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4056 gtl.codepage = CP_ACP;
4057 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4058 ok(ret == strlen(base_string), "ret %d\n",ret);
4060 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
4062 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4063 gtl.codepage = CP_ACP;
4064 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4065 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
4067 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4068 gtl.codepage = CP_ACP;
4069 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4070 ok(ret == strlen(test_string_2), "ret %d\n",ret);
4072 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
4074 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
4075 gtl.codepage = CP_ACP;
4076 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4077 ok(ret == 10, "ret %d\n",ret);
4079 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4080 gtl.codepage = CP_ACP;
4081 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4082 ok(ret == 6, "ret %d\n",ret);
4084 DestroyWindow(hwnd);
4088 /* globals that parent and child access when checking event masks & notifications */
4089 static HWND eventMaskEditHwnd = 0;
4090 static int queriedEventMask;
4091 static int watchForEventMask = 0;
4093 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
4094 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4096 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
4098 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4100 return DefWindowProcA(hwnd, message, wParam, lParam);
4103 /* test event masks in combination with WM_COMMAND */
4104 static void test_eventMask(void)
4109 const char text[] = "foo bar\n";
4112 /* register class to capture WM_COMMAND */
4114 cls.lpfnWndProc = ParentMsgCheckProcA;
4117 cls.hInstance = GetModuleHandleA(0);
4119 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4120 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4121 cls.lpszMenuName = NULL;
4122 cls.lpszClassName = "EventMaskParentClass";
4123 if(!RegisterClassA(&cls)) assert(0);
4125 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4126 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4127 ok (parent != 0, "Failed to create parent window\n");
4129 eventMaskEditHwnd = new_richedit(parent);
4130 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
4132 eventMask = ENM_CHANGE | ENM_UPDATE;
4133 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
4134 ok(ret == ENM_NONE, "wrong event mask\n");
4135 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
4136 ok(ret == eventMask, "failed to set event mask\n");
4138 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
4139 queriedEventMask = 0; /* initialize to something other than we expect */
4140 watchForEventMask = EN_CHANGE;
4141 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
4142 ok(ret == TRUE, "failed to set text\n");
4143 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
4144 notification in response to WM_SETTEXT */
4145 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
4146 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
4150 static int received_WM_NOTIFY = 0;
4151 static int modify_at_WM_NOTIFY = 0;
4152 static HWND hwndRichedit_WM_NOTIFY;
4154 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
4156 if(message == WM_NOTIFY)
4158 received_WM_NOTIFY = 1;
4159 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
4161 return DefWindowProcA(hwnd, message, wParam, lParam);
4164 static void test_WM_NOTIFY(void)
4170 /* register class to capture WM_NOTIFY */
4172 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
4175 cls.hInstance = GetModuleHandleA(0);
4177 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
4178 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
4179 cls.lpszMenuName = NULL;
4180 cls.lpszClassName = "WM_NOTIFY_ParentClass";
4181 if(!RegisterClassA(&cls)) assert(0);
4183 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
4184 0, 0, 200, 60, NULL, NULL, NULL, NULL);
4185 ok (parent != 0, "Failed to create parent window\n");
4187 hwndRichedit_WM_NOTIFY = new_richedit(parent);
4188 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
4190 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
4192 /* Notifications for selection change should only be sent when selection
4193 actually changes. EM_SETCHARFORMAT is one message that calls
4194 ME_CommitUndo, which should check whether message should be sent */
4195 received_WM_NOTIFY = 0;
4196 cf2.cbSize = sizeof(CHARFORMAT2);
4197 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4199 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4200 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4201 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
4202 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4204 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
4206 received_WM_NOTIFY = 0;
4207 modify_at_WM_NOTIFY = 0;
4208 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4209 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
4210 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4212 received_WM_NOTIFY = 0;
4213 modify_at_WM_NOTIFY = 0;
4214 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
4215 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4217 received_WM_NOTIFY = 0;
4218 modify_at_WM_NOTIFY = 0;
4219 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
4220 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
4221 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
4223 DestroyWindow(hwndRichedit_WM_NOTIFY);
4224 DestroyWindow(parent);
4227 START_TEST( editor )
4232 /* Must explicitly LoadLibrary(). The test has no references to functions in
4233 * RICHED20.DLL, so the linker doesn't actually link to it. */
4234 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
4235 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
4239 test_EM_POSFROMCHAR();
4240 test_EM_SCROLLCARET();
4243 test_EM_LINELENGTH();
4244 test_EM_SETCHARFORMAT();
4245 test_EM_SETTEXTMODE();
4246 test_TM_PLAINTEXT();
4247 test_EM_SETOPTIONS();
4249 test_EM_GETTEXTRANGE();
4250 test_EM_GETSELTEXT();
4251 test_EM_SETUNDOLIMIT();
4253 test_EM_SETTEXTEX();
4254 test_EM_LIMITTEXT();
4255 test_EM_EXLIMITTEXT();
4256 test_EM_GETLIMITTEXT();
4258 test_EM_GETMODIFY();
4262 test_EM_STREAMOUT();
4263 test_EM_StreamIn_Undo();
4264 test_EM_FORMATRANGE();
4265 test_unicode_conversions();
4266 test_EM_GETTEXTLENGTHEX();
4267 test_EM_REPLACESEL(1);
4268 test_EM_REPLACESEL(0);
4270 test_EM_AUTOURLDETECT();
4273 /* Set the environment variable WINETEST_RICHED20 to keep windows
4274 * responsive and open for 30 seconds. This is useful for debugging.
4276 * The message pump uses PeekMessage() to empty the queue and then sleeps for
4277 * 50ms before retrying the queue. */
4278 end = time(NULL) + 30;
4279 if (getenv( "WINETEST_RICHED20" )) {
4280 while (time(NULL) < end) {
4281 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
4282 TranslateMessage(&msg);
4283 DispatchMessage(&msg);
4290 OleFlushClipboard();
4291 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());