riched20/tests: Make sure return value is used (LLVM/Clang).
[wine/multimedia.git] / dlls / riched20 / tests / editor.c
bloba91d9849dc0ccb434b3736abe568310a89cdb413
1 /*
2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
36 #define ID_RICHEDITTESTDBUTTON 0x123
38 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
40 #define ok_w3(format, szString1, szString2, szString3) \
41 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
42 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
43 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
44 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
45 format, string1, string2, string3);
47 static HMODULE hmoduleRichEdit;
49 static int is_win9x = 0;
51 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
52 HWND hwnd;
53 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
54 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
55 hmoduleRichEdit, NULL);
56 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
57 return hwnd;
60 static HWND new_richedit(HWND parent) {
61 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
64 /* Keeps the window reponsive for the deley_time in seconds.
65 * This is useful for debugging a test to see what is happening. */
66 static void keep_responsive(time_t delay_time)
68 MSG msg;
69 time_t end;
71 /* The message pump uses PeekMessage() to empty the queue and then
72 * sleeps for 50ms before retrying the queue. */
73 end = time(NULL) + delay_time;
74 while (time(NULL) < end) {
75 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
76 TranslateMessage(&msg);
77 DispatchMessage(&msg);
78 } else {
79 Sleep(50);
84 static void processPendingMessages(void)
86 MSG msg;
87 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
88 TranslateMessage(&msg);
89 DispatchMessage(&msg);
93 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
95 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
96 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
97 SetFocus(hwnd);
98 keybd_event(mod_vk, mod_scan_code, 0, 0);
99 keybd_event(vk, scan_code, 0, 0);
100 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
101 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
102 processPendingMessages();
105 static void simulate_typing_characters(HWND hwnd, const char* szChars)
107 int ret;
109 while (*szChars != '\0') {
110 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
111 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
112 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
113 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
114 szChars++;
118 static BOOL hold_key(int vk)
120 BYTE key_state[256];
121 BOOL result;
123 result = GetKeyboardState(key_state);
124 ok(result, "GetKeyboardState failed.\n");
125 if (!result) return FALSE;
126 key_state[vk] |= 0x80;
127 result = SetKeyboardState(key_state);
128 ok(result, "SetKeyboardState failed.\n");
129 return result != 0;
132 static BOOL release_key(int vk)
134 BYTE key_state[256];
135 BOOL result;
137 result = GetKeyboardState(key_state);
138 ok(result, "GetKeyboardState failed.\n");
139 if (!result) return FALSE;
140 key_state[vk] &= ~0x80;
141 result = SetKeyboardState(key_state);
142 ok(result, "SetKeyboardState failed.\n");
143 return result != 0;
146 static const char haystack[] = "WINEWine wineWine wine WineWine";
147 /* ^0 ^10 ^20 ^30 */
149 struct find_s {
150 int start;
151 int end;
152 const char *needle;
153 int flags;
154 int expected_loc;
158 static struct find_s find_tests[] = {
159 /* Find in empty text */
160 {0, -1, "foo", FR_DOWN, -1},
161 {0, -1, "foo", 0, -1},
162 {0, -1, "", FR_DOWN, -1},
163 {20, 5, "foo", FR_DOWN, -1},
164 {5, 20, "foo", FR_DOWN, -1}
167 static struct find_s find_tests2[] = {
168 /* No-result find */
169 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
170 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
172 /* Subsequent finds */
173 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
174 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
175 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
176 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
178 /* Find backwards */
179 {19, 20, "Wine", FR_MATCHCASE, 13},
180 {10, 20, "Wine", FR_MATCHCASE, 4},
181 {20, 10, "Wine", FR_MATCHCASE, 13},
183 /* Case-insensitive */
184 {1, 31, "wInE", FR_DOWN, 4},
185 {1, 31, "Wine", FR_DOWN, 4},
187 /* High-to-low ranges */
188 {20, 5, "Wine", FR_DOWN, -1},
189 {2, 1, "Wine", FR_DOWN, -1},
190 {30, 29, "Wine", FR_DOWN, -1},
191 {20, 5, "Wine", 0, 13},
193 /* Find nothing */
194 {5, 10, "", FR_DOWN, -1},
195 {10, 5, "", FR_DOWN, -1},
196 {0, -1, "", FR_DOWN, -1},
197 {10, 5, "", 0, -1},
199 /* Whole-word search */
200 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
201 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
202 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
203 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
204 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
205 {11, -1, "winewine", FR_WHOLEWORD, 0},
206 {31, -1, "winewine", FR_WHOLEWORD, 23},
208 /* Bad ranges */
209 {5, 200, "XXX", FR_DOWN, -1},
210 {-20, 20, "Wine", FR_DOWN, -1},
211 {-20, 20, "Wine", FR_DOWN, -1},
212 {-15, -20, "Wine", FR_DOWN, -1},
213 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
215 /* Check the case noted in bug 4479 where matches at end aren't recognized */
216 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
217 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
218 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
219 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
220 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
222 /* The backwards case of bug 4479; bounds look right
223 * Fails because backward find is wrong */
224 {19, 20, "WINE", FR_MATCHCASE, 0},
225 {0, 20, "WINE", FR_MATCHCASE, -1},
227 {0, -1, "wineWine wine", 0, -1},
230 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
231 int findloc;
232 FINDTEXT ft;
233 memset(&ft, 0, sizeof(ft));
234 ft.chrg.cpMin = f->start;
235 ft.chrg.cpMax = f->end;
236 ft.lpstrText = f->needle;
237 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
238 ok(findloc == f->expected_loc,
239 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
240 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
243 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
244 int id) {
245 int findloc;
246 FINDTEXTEX ft;
247 int expected_end_loc;
249 memset(&ft, 0, sizeof(ft));
250 ft.chrg.cpMin = f->start;
251 ft.chrg.cpMax = f->end;
252 ft.lpstrText = f->needle;
253 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
254 ok(findloc == f->expected_loc,
255 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
256 name, id, f->needle, f->start, f->end, f->flags, findloc);
257 ok(ft.chrgText.cpMin == f->expected_loc,
258 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
259 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
260 expected_end_loc = ((f->expected_loc == -1) ? -1
261 : f->expected_loc + strlen(f->needle));
262 ok(ft.chrgText.cpMax == expected_end_loc,
263 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
264 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
267 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
268 int num_tests)
270 int i;
272 for (i = 0; i < num_tests; i++) {
273 check_EM_FINDTEXT(hwnd, name, &find[i], i);
274 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
278 static void test_EM_FINDTEXT(void)
280 HWND hwndRichEdit = new_richedit(NULL);
281 CHARFORMAT2 cf2;
283 /* Empty rich edit control */
284 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
285 sizeof(find_tests)/sizeof(struct find_s));
287 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
289 /* Haystack text */
290 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
291 sizeof(find_tests2)/sizeof(struct find_s));
293 /* Setting a format on an arbitrary range should have no effect in search
294 results. This tests correct offset reporting across runs. */
295 cf2.cbSize = sizeof(CHARFORMAT2);
296 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
297 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
298 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
299 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
300 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
302 /* Haystack text, again */
303 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
304 sizeof(find_tests2)/sizeof(struct find_s));
306 /* Yet another range */
307 cf2.dwMask = CFM_BOLD | cf2.dwMask;
308 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
309 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
310 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
312 /* Haystack text, again */
313 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
314 sizeof(find_tests2)/sizeof(struct find_s));
316 DestroyWindow(hwndRichEdit);
319 static const struct getline_s {
320 int line;
321 size_t buffer_len;
322 const char *text;
323 } gl[] = {
324 {0, 10, "foo bar\r"},
325 {1, 10, "\r"},
326 {2, 10, "bar\r"},
327 {3, 10, "\r"},
329 /* Buffer smaller than line length */
330 {0, 2, "foo bar\r"},
331 {0, 1, "foo bar\r"},
332 {0, 0, "foo bar\r"}
335 static void test_EM_GETLINE(void)
337 int i;
338 HWND hwndRichEdit = new_richedit(NULL);
339 static const int nBuf = 1024;
340 char dest[1024], origdest[1024];
341 const char text[] = "foo bar\n"
342 "\n"
343 "bar\n";
345 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
347 memset(origdest, 0xBB, nBuf);
348 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
350 int nCopied;
351 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
352 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
353 memset(dest, 0xBB, nBuf);
354 *(WORD *) dest = gl[i].buffer_len;
356 /* EM_GETLINE appends a "\r\0" to the end of the line
357 * nCopied counts up to and including the '\r' */
358 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
359 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
360 expected_nCopied);
361 /* two special cases since a parameter is passed via dest */
362 if (gl[i].buffer_len == 0)
363 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
364 "buffer_len=0\n");
365 else if (gl[i].buffer_len == 1)
366 ok(dest[0] == gl[i].text[0] && !dest[1] &&
367 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
368 else
370 /* Prepare hex strings of buffers to dump on failure. */
371 char expectedbuf[1024];
372 char resultbuf[1024];
373 int j;
374 resultbuf[0] = '\0';
375 for (j = 0; j < 32; j++)
376 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
377 expectedbuf[0] = '\0';
378 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
379 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
380 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
381 sprintf(expectedbuf+strlen(expectedbuf), "??");
382 for (; j < 32; j++) /* Bytes after declared buffer size */
383 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
385 /* Test the part of the buffer that is expected to be written according
386 * to the MSDN documentation fo EM_GETLINE, which does not state that
387 * a NULL terminating character will be added unless no text is copied.
389 * Windows 95, 98 & NT do not append a NULL terminating character, but
390 * Windows 2000 and up do append a NULL terminating character if there
391 * is space in the buffer. The test will ignore this difference. */
392 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
393 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
394 i, expected_bytes_written, expectedbuf, resultbuf);
395 /* Test the part of the buffer after the declared length to make sure
396 * there are no buffer overruns. */
397 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
398 nBuf - gl[i].buffer_len),
399 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
400 i, expected_bytes_written, expectedbuf, resultbuf);
404 DestroyWindow(hwndRichEdit);
407 static void test_EM_LINELENGTH(void)
409 HWND hwndRichEdit = new_richedit(NULL);
410 const char * text =
411 "richedit1\r"
412 "richedit1\n"
413 "richedit1\r\n"
414 "richedit1";
415 int offset_test[10][2] = {
416 {0, 9},
417 {5, 9},
418 {10, 9},
419 {15, 9},
420 {20, 9},
421 {25, 9},
422 {30, 9},
423 {35, 9},
424 {40, 0},
425 {45, 0},
427 int i;
428 LRESULT result;
430 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
432 for (i = 0; i < 10; i++) {
433 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
434 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
435 offset_test[i][0], result, offset_test[i][1]);
438 DestroyWindow(hwndRichEdit);
441 static int get_scroll_pos_y(HWND hwnd)
443 POINT p = {-1, -1};
444 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
445 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
446 return p.y;
449 static void move_cursor(HWND hwnd, LONG charindex)
451 CHARRANGE cr;
452 cr.cpMax = charindex;
453 cr.cpMin = charindex;
454 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
457 static void line_scroll(HWND hwnd, int amount)
459 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
462 static void test_EM_SCROLLCARET(void)
464 int prevY, curY;
465 const char text[] = "aa\n"
466 "this is a long line of text that should be longer than the "
467 "control's width\n"
468 "cc\n"
469 "dd\n"
470 "ee\n"
471 "ff\n"
472 "gg\n"
473 "hh\n";
474 /* The richedit window height needs to be large enough vertically to fit in
475 * more than two lines of text, so the new_richedit function can't be used
476 * since a height of 60 was not large enough on some systems.
478 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
479 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
480 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
481 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
483 /* Can't verify this */
484 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
486 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
488 /* Caret above visible window */
489 line_scroll(hwndRichEdit, 3);
490 prevY = get_scroll_pos_y(hwndRichEdit);
491 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
492 curY = get_scroll_pos_y(hwndRichEdit);
493 ok(prevY != curY, "%d == %d\n", prevY, curY);
495 /* Caret below visible window */
496 move_cursor(hwndRichEdit, sizeof(text) - 1);
497 line_scroll(hwndRichEdit, -3);
498 prevY = get_scroll_pos_y(hwndRichEdit);
499 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
500 curY = get_scroll_pos_y(hwndRichEdit);
501 ok(prevY != curY, "%d == %d\n", prevY, curY);
503 /* Caret in visible window */
504 move_cursor(hwndRichEdit, sizeof(text) - 2);
505 prevY = get_scroll_pos_y(hwndRichEdit);
506 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
507 curY = get_scroll_pos_y(hwndRichEdit);
508 ok(prevY == curY, "%d != %d\n", prevY, curY);
510 /* Caret still in visible window */
511 line_scroll(hwndRichEdit, -1);
512 prevY = get_scroll_pos_y(hwndRichEdit);
513 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
514 curY = get_scroll_pos_y(hwndRichEdit);
515 ok(prevY == curY, "%d != %d\n", prevY, curY);
517 DestroyWindow(hwndRichEdit);
520 static void test_EM_POSFROMCHAR(void)
522 HWND hwndRichEdit = new_richedit(NULL);
523 int i, expected;
524 LRESULT result;
525 unsigned int height = 0;
526 int xpos = 0;
527 POINTL pt;
528 LOCALESIGNATURE sig;
529 BOOL rtl;
530 static const char text[] = "aa\n"
531 "this is a long line of text that should be longer than the "
532 "control's width\n"
533 "cc\n"
534 "dd\n"
535 "ee\n"
536 "ff\n"
537 "gg\n"
538 "hh\n";
540 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
541 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
542 (sig.lsUsb[3] & 0x08000000) != 0);
544 /* Fill the control to lines to ensure that most of them are offscreen */
545 for (i = 0; i < 50; i++)
547 /* Do not modify the string; it is exactly 16 characters long. */
548 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
549 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
553 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
554 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
555 Richedit 3.0 accepts either of the above API conventions.
558 /* Testing Richedit 2.0 API format */
560 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
561 Since all lines are identical and drawn with the same font,
562 they should have the same height... right?
564 for (i = 0; i < 50; i++)
566 /* All the lines are 16 characters long */
567 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
568 if (i == 0)
570 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
571 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
572 xpos = LOWORD(result);
574 else if (i == 1)
576 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
577 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
578 height = HIWORD(result);
580 else
582 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
583 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
587 /* Testing position at end of text */
588 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
589 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
590 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
592 /* Testing position way past end of text */
593 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
594 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
595 expected = (rtl ? 8 : 1);
596 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
598 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
599 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
600 for (i = 0; i < 50; i++)
602 /* All the lines are 16 characters long */
603 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
604 ok((signed short)(HIWORD(result)) == (i - 1) * height,
605 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
606 (signed short)(HIWORD(result)), (i - 1) * height);
607 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
610 /* Testing position at end of text */
611 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
612 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
613 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
615 /* Testing position way past end of text */
616 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
617 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
618 expected = (rtl ? 8 : 1);
619 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
621 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
622 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
623 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
625 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
626 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
627 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
628 xpos = LOWORD(result);
630 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
631 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
632 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
633 ok((signed short)(LOWORD(result)) < xpos,
634 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
635 (signed short)(LOWORD(result)), xpos);
636 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
638 /* Test around end of text that doesn't end in a newline. */
639 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
640 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
641 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
642 ok(pt.x > 1, "pt.x = %d\n", pt.x);
643 xpos = pt.x;
644 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
645 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
646 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
647 xpos = (rtl ? pt.x + 7 : pt.x);
648 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
649 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
650 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
652 /* Try a negative position. */
653 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
654 ok(pt.x == 1, "pt.x = %d\n", pt.x);
656 DestroyWindow(hwndRichEdit);
659 static void test_EM_SETCHARFORMAT(void)
661 HWND hwndRichEdit = new_richedit(NULL);
662 CHARFORMAT2 cf2;
663 int rc = 0;
664 int tested_effects[] = {
665 CFE_BOLD,
666 CFE_ITALIC,
667 CFE_UNDERLINE,
668 CFE_STRIKEOUT,
669 CFE_PROTECTED,
670 CFE_LINK,
671 CFE_SUBSCRIPT,
672 CFE_SUPERSCRIPT,
675 int i;
676 CHARRANGE cr;
677 LOCALESIGNATURE sig;
678 BOOL rtl;
680 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
681 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
682 (sig.lsUsb[3] & 0x08000000) != 0);
684 /* Invalid flags, CHARFORMAT2 structure blanked out */
685 memset(&cf2, 0, sizeof(cf2));
686 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
687 (LPARAM) &cf2);
688 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
690 /* A valid flag, CHARFORMAT2 structure blanked out */
691 memset(&cf2, 0, sizeof(cf2));
692 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
693 (LPARAM) &cf2);
694 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
696 /* A valid flag, CHARFORMAT2 structure blanked out */
697 memset(&cf2, 0, sizeof(cf2));
698 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
699 (LPARAM) &cf2);
700 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
702 /* A valid flag, CHARFORMAT2 structure blanked out */
703 memset(&cf2, 0, sizeof(cf2));
704 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
705 (LPARAM) &cf2);
706 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
708 /* A valid flag, CHARFORMAT2 structure blanked out */
709 memset(&cf2, 0, sizeof(cf2));
710 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
711 (LPARAM) &cf2);
712 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
714 /* Invalid flags, CHARFORMAT2 structure minimally filled */
715 memset(&cf2, 0, sizeof(cf2));
716 cf2.cbSize = sizeof(CHARFORMAT2);
717 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
718 (LPARAM) &cf2);
719 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
720 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
721 ok(rc == FALSE, "Should not be able to undo here.\n");
722 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
724 /* A valid flag, CHARFORMAT2 structure minimally filled */
725 memset(&cf2, 0, sizeof(cf2));
726 cf2.cbSize = sizeof(CHARFORMAT2);
727 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
728 (LPARAM) &cf2);
729 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
730 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
731 ok(rc == FALSE, "Should not be able to undo here.\n");
732 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
734 /* A valid flag, CHARFORMAT2 structure minimally filled */
735 memset(&cf2, 0, sizeof(cf2));
736 cf2.cbSize = sizeof(CHARFORMAT2);
737 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
738 (LPARAM) &cf2);
739 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
740 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
741 ok(rc == FALSE, "Should not be able to undo here.\n");
742 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
744 /* A valid flag, CHARFORMAT2 structure minimally filled */
745 memset(&cf2, 0, sizeof(cf2));
746 cf2.cbSize = sizeof(CHARFORMAT2);
747 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
748 (LPARAM) &cf2);
749 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
750 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
751 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
752 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
754 /* A valid flag, CHARFORMAT2 structure minimally filled */
755 memset(&cf2, 0, sizeof(cf2));
756 cf2.cbSize = sizeof(CHARFORMAT2);
757 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
758 (LPARAM) &cf2);
759 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
760 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
761 ok(rc == TRUE, "Should not be able to undo here.\n");
762 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
764 cf2.cbSize = sizeof(CHARFORMAT2);
765 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
766 (LPARAM) &cf2);
768 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
769 cf2.cbSize = sizeof(CHARFORMAT2);
770 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
771 (LPARAM) &cf2);
772 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
773 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
775 /* wParam==0 is default char format, does not set modify */
776 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
777 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
778 ok(rc == 0, "Text marked as modified, expected not modified!\n");
779 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
780 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
781 if (! rtl)
783 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
784 ok(rc == 0, "Text marked as modified, expected not modified!\n");
786 else
787 skip("RTL language found\n");
789 /* wParam==SCF_SELECTION sets modify if nonempty selection */
790 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
791 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
792 ok(rc == 0, "Text marked as modified, expected not modified!\n");
793 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
794 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
795 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
796 ok(rc == 0, "Text marked as modified, expected not modified!\n");
798 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
799 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
800 ok(rc == 0, "Text marked as modified, expected not modified!\n");
801 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
802 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
803 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
804 ok(rc == 0, "Text marked as modified, expected not modified!\n");
805 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
806 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
807 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
808 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
809 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
811 /* wParam==SCF_ALL sets modify regardless of whether text is present */
812 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
813 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
814 ok(rc == 0, "Text marked as modified, expected not modified!\n");
815 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
816 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
817 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
818 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
820 DestroyWindow(hwndRichEdit);
822 /* EM_GETCHARFORMAT tests */
823 for (i = 0; tested_effects[i]; i++)
825 hwndRichEdit = new_richedit(NULL);
826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
828 /* Need to set a TrueType font to get consistent CFM_BOLD results */
829 memset(&cf2, 0, sizeof(CHARFORMAT2));
830 cf2.cbSize = sizeof(CHARFORMAT2);
831 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
832 cf2.dwEffects = 0;
833 strcpy(cf2.szFaceName, "Courier New");
834 cf2.wWeight = FW_DONTCARE;
835 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
837 memset(&cf2, 0, sizeof(CHARFORMAT2));
838 cf2.cbSize = sizeof(CHARFORMAT2);
839 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
840 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
841 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
842 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
844 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
845 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
846 ok((cf2.dwEffects & tested_effects[i]) == 0,
847 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
849 memset(&cf2, 0, sizeof(CHARFORMAT2));
850 cf2.cbSize = sizeof(CHARFORMAT2);
851 cf2.dwMask = tested_effects[i];
852 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
853 cf2.dwMask = CFM_SUPERSCRIPT;
854 cf2.dwEffects = tested_effects[i];
855 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
856 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
858 memset(&cf2, 0, sizeof(CHARFORMAT2));
859 cf2.cbSize = sizeof(CHARFORMAT2);
860 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
861 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
862 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
863 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
865 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
866 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
867 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
868 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
870 memset(&cf2, 0, sizeof(CHARFORMAT2));
871 cf2.cbSize = sizeof(CHARFORMAT2);
872 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
873 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
874 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
875 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
877 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
878 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
879 ok((cf2.dwEffects & tested_effects[i]) == 0,
880 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
882 memset(&cf2, 0, sizeof(CHARFORMAT2));
883 cf2.cbSize = sizeof(CHARFORMAT2);
884 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
885 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
886 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
887 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
889 (cf2.dwMask & tested_effects[i]) == 0),
890 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
892 DestroyWindow(hwndRichEdit);
895 for (i = 0; tested_effects[i]; i++)
897 hwndRichEdit = new_richedit(NULL);
898 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
900 /* Need to set a TrueType font to get consistent CFM_BOLD results */
901 memset(&cf2, 0, sizeof(CHARFORMAT2));
902 cf2.cbSize = sizeof(CHARFORMAT2);
903 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
904 cf2.dwEffects = 0;
905 strcpy(cf2.szFaceName, "Courier New");
906 cf2.wWeight = FW_DONTCARE;
907 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
909 memset(&cf2, 0, sizeof(CHARFORMAT2));
910 cf2.cbSize = sizeof(CHARFORMAT2);
911 cf2.dwMask = tested_effects[i];
912 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
913 cf2.dwMask = CFM_SUPERSCRIPT;
914 cf2.dwEffects = tested_effects[i];
915 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
916 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
918 memset(&cf2, 0, sizeof(CHARFORMAT2));
919 cf2.cbSize = sizeof(CHARFORMAT2);
920 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
921 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
922 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
923 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
925 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
926 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
927 ok((cf2.dwEffects & tested_effects[i]) == 0,
928 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
930 memset(&cf2, 0, sizeof(CHARFORMAT2));
931 cf2.cbSize = sizeof(CHARFORMAT2);
932 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
933 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
934 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
935 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
937 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
938 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
939 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
940 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
942 memset(&cf2, 0, sizeof(CHARFORMAT2));
943 cf2.cbSize = sizeof(CHARFORMAT2);
944 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
945 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
946 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
947 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
949 (cf2.dwMask & tested_effects[i]) == 0),
950 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
951 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
952 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
954 DestroyWindow(hwndRichEdit);
957 /* Effects applied on an empty selection should take effect when selection is
958 replaced with text */
959 hwndRichEdit = new_richedit(NULL);
960 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
961 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
963 memset(&cf2, 0, sizeof(CHARFORMAT2));
964 cf2.cbSize = sizeof(CHARFORMAT2);
965 cf2.dwMask = CFM_BOLD;
966 cf2.dwEffects = CFE_BOLD;
967 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
969 /* Selection is now nonempty */
970 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
972 memset(&cf2, 0, sizeof(CHARFORMAT2));
973 cf2.cbSize = sizeof(CHARFORMAT2);
974 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
975 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
977 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
978 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
979 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
980 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
983 /* Set two effects on an empty selection */
984 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
985 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
987 memset(&cf2, 0, sizeof(CHARFORMAT2));
988 cf2.cbSize = sizeof(CHARFORMAT2);
989 cf2.dwMask = CFM_BOLD;
990 cf2.dwEffects = CFE_BOLD;
991 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
992 cf2.dwMask = CFM_ITALIC;
993 cf2.dwEffects = CFE_ITALIC;
994 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
996 /* Selection is now nonempty */
997 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
999 memset(&cf2, 0, sizeof(CHARFORMAT2));
1000 cf2.cbSize = sizeof(CHARFORMAT2);
1001 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1002 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1004 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1005 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1006 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1007 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1009 /* Setting the (empty) selection to exactly the same place as before should
1010 NOT clear the insertion style! */
1011 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1012 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1014 memset(&cf2, 0, sizeof(CHARFORMAT2));
1015 cf2.cbSize = sizeof(CHARFORMAT2);
1016 cf2.dwMask = CFM_BOLD;
1017 cf2.dwEffects = CFE_BOLD;
1018 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1020 /* Empty selection in same place, insert style should NOT be forgotten here. */
1021 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1023 /* Selection is now nonempty */
1024 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1026 memset(&cf2, 0, sizeof(CHARFORMAT2));
1027 cf2.cbSize = sizeof(CHARFORMAT2);
1028 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1029 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1031 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1032 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1033 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1034 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1036 /* Ditto with EM_EXSETSEL */
1037 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1038 cr.cpMin = 2; cr.cpMax = 2;
1039 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1041 memset(&cf2, 0, sizeof(CHARFORMAT2));
1042 cf2.cbSize = sizeof(CHARFORMAT2);
1043 cf2.dwMask = CFM_BOLD;
1044 cf2.dwEffects = CFE_BOLD;
1045 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1047 /* Empty selection in same place, insert style should NOT be forgotten here. */
1048 cr.cpMin = 2; cr.cpMax = 2;
1049 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1051 /* Selection is now nonempty */
1052 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1054 memset(&cf2, 0, sizeof(CHARFORMAT2));
1055 cf2.cbSize = sizeof(CHARFORMAT2);
1056 cr.cpMin = 2; cr.cpMax = 6;
1057 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1058 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1060 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1061 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1062 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1063 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1065 DestroyWindow(hwndRichEdit);
1068 static void test_EM_SETTEXTMODE(void)
1070 HWND hwndRichEdit = new_richedit(NULL);
1071 CHARFORMAT2 cf2, cf2test;
1072 CHARRANGE cr;
1073 int rc = 0;
1075 /*Attempt to use mutually exclusive modes*/
1076 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT|TM_RICHTEXT, 0);
1077 ok(rc == E_INVALIDARG,
1078 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1080 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1081 /*Insert text into the control*/
1083 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1085 /*Attempt to change the control to plain text mode*/
1086 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1087 ok(rc == E_UNEXPECTED,
1088 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1090 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1091 If rich text is pasted, it should have the same formatting as the rest
1092 of the text in the control*/
1094 /*Italicize the text
1095 *NOTE: If the default text was already italicized, the test will simply
1096 reverse; in other words, it will copy a regular "wine" into a plain
1097 text window that uses an italicized format*/
1098 cf2.cbSize = sizeof(CHARFORMAT2);
1099 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1100 (LPARAM) &cf2);
1102 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1103 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1105 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1106 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1108 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1109 however, SCF_ALL has been implemented*/
1110 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1111 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1113 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1114 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1116 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1118 /*Select the string "wine"*/
1119 cr.cpMin = 0;
1120 cr.cpMax = 4;
1121 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1123 /*Copy the italicized "wine" to the clipboard*/
1124 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1126 /*Reset the formatting to default*/
1127 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1128 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1129 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1131 /*Clear the text in the control*/
1132 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1134 /*Switch to Plain Text Mode*/
1135 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1136 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1138 /*Input "wine" again in normal format*/
1139 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1141 /*Paste the italicized "wine" into the control*/
1142 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1144 /*Select a character from the first "wine" string*/
1145 cr.cpMin = 2;
1146 cr.cpMax = 3;
1147 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1149 /*Retrieve its formatting*/
1150 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1151 (LPARAM) &cf2);
1153 /*Select a character from the second "wine" string*/
1154 cr.cpMin = 5;
1155 cr.cpMax = 6;
1156 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1158 /*Retrieve its formatting*/
1159 cf2test.cbSize = sizeof(CHARFORMAT2);
1160 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1161 (LPARAM) &cf2test);
1163 /*Compare the two formattings*/
1164 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1165 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1166 cf2.dwEffects, cf2test.dwEffects);
1167 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1168 printing "wine" in the current format(normal)
1169 pasting "wine" from the clipboard(italicized)
1170 comparing the two formats(should differ)*/
1172 /*Attempt to switch with text in control*/
1173 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1174 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1176 /*Clear control*/
1177 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1179 /*Switch into Rich Text mode*/
1180 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1181 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1183 /*Print "wine" in normal formatting into the control*/
1184 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1186 /*Paste italicized "wine" into the control*/
1187 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1189 /*Select text from the first "wine" string*/
1190 cr.cpMin = 1;
1191 cr.cpMax = 3;
1192 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1194 /*Retrieve its formatting*/
1195 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1196 (LPARAM) &cf2);
1198 /*Select text from the second "wine" string*/
1199 cr.cpMin = 6;
1200 cr.cpMax = 7;
1201 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1203 /*Retrieve its formatting*/
1204 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1205 (LPARAM) &cf2test);
1207 /*Test that the two formattings are not the same*/
1208 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1209 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1210 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1212 DestroyWindow(hwndRichEdit);
1215 static void test_SETPARAFORMAT(void)
1217 HWND hwndRichEdit = new_richedit(NULL);
1218 PARAFORMAT2 fmt;
1219 HRESULT ret;
1220 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1221 fmt.cbSize = sizeof(PARAFORMAT2);
1222 fmt.dwMask = PFM_ALIGNMENT;
1223 fmt.wAlignment = PFA_LEFT;
1225 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1226 ok(ret != 0, "expected non-zero got %d\n", ret);
1228 fmt.cbSize = sizeof(PARAFORMAT2);
1229 fmt.dwMask = -1;
1230 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1231 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1232 * between richedit different native builds of riched20.dll
1233 * used on different Windows versions. */
1234 ret &= ~PFM_TABLEROWDELIMITER;
1235 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1237 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1238 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1240 DestroyWindow(hwndRichEdit);
1243 static void test_TM_PLAINTEXT(void)
1245 /*Tests plain text properties*/
1247 HWND hwndRichEdit = new_richedit(NULL);
1248 CHARFORMAT2 cf2, cf2test;
1249 CHARRANGE cr;
1250 int rc = 0;
1252 /*Switch to plain text mode*/
1254 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1255 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1257 /*Fill control with text*/
1259 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1261 /*Select some text and bold it*/
1263 cr.cpMin = 10;
1264 cr.cpMax = 20;
1265 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1266 cf2.cbSize = sizeof(CHARFORMAT2);
1267 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1269 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1270 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1272 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1273 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1275 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1276 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1278 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1279 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1281 /*Get the formatting of those characters*/
1283 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1285 /*Get the formatting of some other characters*/
1286 cf2test.cbSize = sizeof(CHARFORMAT2);
1287 cr.cpMin = 21;
1288 cr.cpMax = 30;
1289 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1290 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1292 /*Test that they are the same as plain text allows only one formatting*/
1294 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1295 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1296 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1298 /*Fill the control with a "wine" string, which when inserted will be bold*/
1300 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1302 /*Copy the bolded "wine" string*/
1304 cr.cpMin = 0;
1305 cr.cpMax = 4;
1306 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1307 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1309 /*Swap back to rich text*/
1311 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1312 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1314 /*Set the default formatting to bold italics*/
1316 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1317 cf2.dwMask |= CFM_ITALIC;
1318 cf2.dwEffects ^= CFE_ITALIC;
1319 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1320 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1322 /*Set the text in the control to "wine", which will be bold and italicized*/
1324 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1326 /*Paste the plain text "wine" string, which should take the insert
1327 formatting, which at the moment is bold italics*/
1329 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1331 /*Select the first "wine" string and retrieve its formatting*/
1333 cr.cpMin = 1;
1334 cr.cpMax = 3;
1335 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1336 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1338 /*Select the second "wine" string and retrieve its formatting*/
1340 cr.cpMin = 5;
1341 cr.cpMax = 7;
1342 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1343 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1345 /*Compare the two formattings. They should be the same.*/
1347 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1348 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1349 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1350 DestroyWindow(hwndRichEdit);
1353 static void test_WM_GETTEXT(void)
1355 HWND hwndRichEdit = new_richedit(NULL);
1356 static const char text[] = "Hello. My name is RichEdit!";
1357 static const char text2[] = "Hello. My name is RichEdit!\r";
1358 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1359 char buffer[1024] = {0};
1360 int result;
1362 /* Baseline test with normal-sized buffer */
1363 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1364 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1365 ok(result == lstrlen(buffer),
1366 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1367 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1368 result = strcmp(buffer,text);
1369 ok(result == 0,
1370 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1372 /* Test for returned value of WM_GETTEXTLENGTH */
1373 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1374 ok(result == lstrlen(text),
1375 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1376 result, lstrlen(text));
1378 /* Test for behavior in overflow case */
1379 memset(buffer, 0, 1024);
1380 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1381 ok(result == 0 ||
1382 result == lstrlenA(text) - 1, /* XP, win2k3 */
1383 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1384 result = strcmp(buffer,text);
1385 if (result)
1386 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1387 ok(result == 0,
1388 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1390 /* Baseline test with normal-sized buffer and carriage return */
1391 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1392 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1393 ok(result == lstrlen(buffer),
1394 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1395 result = strcmp(buffer,text2_after);
1396 ok(result == 0,
1397 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1399 /* Test for returned value of WM_GETTEXTLENGTH */
1400 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1401 ok(result == lstrlen(text2_after),
1402 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1403 result, lstrlen(text2_after));
1405 /* Test for behavior of CRLF conversion in case of overflow */
1406 memset(buffer, 0, 1024);
1407 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1408 ok(result == 0 ||
1409 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1410 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1411 result = strcmp(buffer,text2);
1412 if (result)
1413 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1414 ok(result == 0,
1415 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1417 DestroyWindow(hwndRichEdit);
1420 static void test_EM_GETTEXTRANGE(void)
1422 HWND hwndRichEdit = new_richedit(NULL);
1423 const char * text1 = "foo bar\r\nfoo bar";
1424 const char * text2 = "foo bar\rfoo bar";
1425 const char * expect = "bar\rfoo";
1426 char buffer[1024] = {0};
1427 LRESULT result;
1428 TEXTRANGEA textRange;
1430 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1432 textRange.lpstrText = buffer;
1433 textRange.chrg.cpMin = 4;
1434 textRange.chrg.cpMax = 11;
1435 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1436 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1437 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1439 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1441 textRange.lpstrText = buffer;
1442 textRange.chrg.cpMin = 4;
1443 textRange.chrg.cpMax = 11;
1444 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1445 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1446 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1448 /* cpMax of text length is used instead of -1 in this case */
1449 textRange.lpstrText = buffer;
1450 textRange.chrg.cpMin = 0;
1451 textRange.chrg.cpMax = -1;
1452 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1453 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1454 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1456 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1457 textRange.lpstrText = buffer;
1458 textRange.chrg.cpMin = -1;
1459 textRange.chrg.cpMax = 1;
1460 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1461 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1462 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1464 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1465 textRange.lpstrText = buffer;
1466 textRange.chrg.cpMin = 1;
1467 textRange.chrg.cpMax = -1;
1468 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1469 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1470 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1472 /* no end character is copied if cpMax - cpMin < 0 */
1473 textRange.lpstrText = buffer;
1474 textRange.chrg.cpMin = 5;
1475 textRange.chrg.cpMax = 5;
1476 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1477 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1478 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1480 /* cpMax of text length is used if cpMax > text length*/
1481 textRange.lpstrText = buffer;
1482 textRange.chrg.cpMin = 0;
1483 textRange.chrg.cpMax = 1000;
1484 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1485 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1486 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1488 DestroyWindow(hwndRichEdit);
1491 static void test_EM_GETSELTEXT(void)
1493 HWND hwndRichEdit = new_richedit(NULL);
1494 const char * text1 = "foo bar\r\nfoo bar";
1495 const char * text2 = "foo bar\rfoo bar";
1496 const char * expect = "bar\rfoo";
1497 char buffer[1024] = {0};
1498 LRESULT result;
1500 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1502 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1503 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1504 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1505 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1507 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1509 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1510 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1511 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1512 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1514 DestroyWindow(hwndRichEdit);
1517 /* FIXME: need to test unimplemented options and robustly test wparam */
1518 static void test_EM_SETOPTIONS(void)
1520 HWND hwndRichEdit;
1521 static const char text[] = "Hello. My name is RichEdit!";
1522 char buffer[1024] = {0};
1523 DWORD dwStyle, options, oldOptions;
1524 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1525 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1526 ES_SELECTIONBAR|ES_VERTICAL;
1528 /* Test initial options. */
1529 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1530 0, 0, 200, 60, NULL, NULL,
1531 hmoduleRichEdit, NULL);
1532 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1533 RICHEDIT_CLASS, (int) GetLastError());
1534 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1535 ok(options == 0, "Incorrect initial options %x\n", options);
1536 DestroyWindow(hwndRichEdit);
1538 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1539 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1540 0, 0, 200, 60, NULL, NULL,
1541 hmoduleRichEdit, NULL);
1542 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1543 RICHEDIT_CLASS, (int) GetLastError());
1544 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1545 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1546 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1547 "Incorrect initial options %x\n", options);
1549 /* NEGATIVE TESTING - NO OPTIONS SET */
1550 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1551 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1553 /* testing no readonly by sending 'a' to the control*/
1554 SetFocus(hwndRichEdit);
1555 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1556 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1557 ok(buffer[0]=='a',
1558 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1559 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1561 /* READONLY - sending 'a' to the control */
1562 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1563 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1564 SetFocus(hwndRichEdit);
1565 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1566 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1567 ok(buffer[0]==text[0],
1568 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1570 /* EM_SETOPTIONS changes the window style, but changing the
1571 * window style does not change the options. */
1572 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1573 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1574 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1575 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1576 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1577 /* Confirm that the text is still read only. */
1578 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1579 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1580 ok(buffer[0]==text[0],
1581 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1583 oldOptions = options;
1584 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1585 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1586 ok(options == oldOptions,
1587 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1589 DestroyWindow(hwndRichEdit);
1592 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1594 CHARFORMAT2W text_format;
1595 text_format.cbSize = sizeof(text_format);
1596 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1597 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1598 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1601 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1603 int link_present = 0;
1605 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1606 if (is_url)
1607 { /* control text is url; should get CFE_LINK */
1608 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1610 else
1612 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1616 static HWND new_static_wnd(HWND parent) {
1617 return new_window("Static", 0, parent);
1620 static void test_EM_AUTOURLDETECT(void)
1622 /* DO NOT change the properties of the first two elements. To shorten the
1623 tests, all tests after WM_SETTEXT test just the first two elements -
1624 one non-URL and one URL */
1625 struct urls_s {
1626 const char *text;
1627 int is_url;
1628 } urls[12] = {
1629 {"winehq.org", 0},
1630 {"http://www.winehq.org", 1},
1631 {"http//winehq.org", 0},
1632 {"ww.winehq.org", 0},
1633 {"www.winehq.org", 1},
1634 {"ftp://192.168.1.1", 1},
1635 {"ftp//192.168.1.1", 0},
1636 {"mailto:your@email.com", 1},
1637 {"prospero:prosperoserver", 1},
1638 {"telnet:test", 1},
1639 {"news:newserver", 1},
1640 {"wais:waisserver", 1}
1643 int i, j;
1644 int urlRet=-1;
1645 HWND hwndRichEdit, parent;
1647 /* All of the following should cause the URL to be detected */
1648 const char * templates_delim[] = {
1649 "This is some text with X on it",
1650 "This is some text with (X) on it",
1651 "This is some text with X\r on it",
1652 "This is some text with ---X--- on it",
1653 "This is some text with \"X\" on it",
1654 "This is some text with 'X' on it",
1655 "This is some text with 'X' on it",
1656 "This is some text with :X: on it",
1658 "This text ends with X",
1660 "This is some text with X) on it",
1661 "This is some text with X--- on it",
1662 "This is some text with X\" on it",
1663 "This is some text with X' on it",
1664 "This is some text with X: on it",
1666 "This is some text with (X on it",
1667 "This is some text with \rX on it",
1668 "This is some text with ---X on it",
1669 "This is some text with \"X on it",
1670 "This is some text with 'X on it",
1671 "This is some text with :X on it",
1673 /* None of these should cause the URL to be detected */
1674 const char * templates_non_delim[] = {
1675 "This is some text with |X| on it",
1676 "This is some text with *X* on it",
1677 "This is some text with /X/ on it",
1678 "This is some text with +X+ on it",
1679 "This is some text with %X% on it",
1680 "This is some text with #X# on it",
1681 "This is some text with @X@ on it",
1682 "This is some text with \\X\\ on it",
1683 "This is some text with |X on it",
1684 "This is some text with *X on it",
1685 "This is some text with /X on it",
1686 "This is some text with +X on it",
1687 "This is some text with %X on it",
1688 "This is some text with #X on it",
1689 "This is some text with @X on it",
1690 "This is some text with \\X on it",
1692 /* All of these cause the URL detection to be extended by one more byte,
1693 thus demonstrating that the tested character is considered as part
1694 of the URL. */
1695 const char * templates_xten_delim[] = {
1696 "This is some text with X| on it",
1697 "This is some text with X* on it",
1698 "This is some text with X/ on it",
1699 "This is some text with X+ on it",
1700 "This is some text with X% on it",
1701 "This is some text with X# on it",
1702 "This is some text with X@ on it",
1703 "This is some text with X\\ on it",
1705 char buffer[1024];
1707 parent = new_static_wnd(NULL);
1708 hwndRichEdit = new_richedit(parent);
1709 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1710 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1711 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1712 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1713 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1714 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1715 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1716 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1717 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1718 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1719 /* for each url, check the text to see if CFE_LINK effect is present */
1720 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1722 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1723 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1724 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1726 /* Link detection should happen immediately upon WM_SETTEXT */
1727 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1728 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1729 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1731 DestroyWindow(hwndRichEdit);
1733 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1734 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1735 hwndRichEdit = new_richedit(parent);
1737 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1738 char * at_pos;
1739 int at_offset;
1740 int end_offset;
1742 at_pos = strchr(templates_delim[j], 'X');
1743 at_offset = at_pos - templates_delim[j];
1744 strncpy(buffer, templates_delim[j], at_offset);
1745 buffer[at_offset] = '\0';
1746 strcat(buffer, urls[i].text);
1747 strcat(buffer, templates_delim[j] + at_offset + 1);
1748 end_offset = at_offset + strlen(urls[i].text);
1750 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1751 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1753 /* This assumes no templates start with the URL itself, and that they
1754 have at least two characters before the URL text */
1755 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1756 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1757 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1758 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1759 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1760 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1762 if (urls[i].is_url)
1764 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1765 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1766 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1767 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1769 else
1771 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1772 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1773 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1774 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1776 if (buffer[end_offset] != '\0')
1778 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1779 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1780 if (buffer[end_offset +1] != '\0')
1782 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1783 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1788 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1789 char * at_pos;
1790 int at_offset;
1791 int end_offset;
1793 at_pos = strchr(templates_non_delim[j], 'X');
1794 at_offset = at_pos - templates_non_delim[j];
1795 strncpy(buffer, templates_non_delim[j], at_offset);
1796 buffer[at_offset] = '\0';
1797 strcat(buffer, urls[i].text);
1798 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1799 end_offset = at_offset + strlen(urls[i].text);
1801 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1802 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1804 /* This assumes no templates start with the URL itself, and that they
1805 have at least two characters before the URL text */
1806 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1807 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1808 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1809 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1810 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1811 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1813 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1814 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1815 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1816 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1817 if (buffer[end_offset] != '\0')
1819 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1820 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1821 if (buffer[end_offset +1] != '\0')
1823 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1824 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1829 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1830 char * at_pos;
1831 int at_offset;
1832 int end_offset;
1834 at_pos = strchr(templates_xten_delim[j], 'X');
1835 at_offset = at_pos - templates_xten_delim[j];
1836 strncpy(buffer, templates_xten_delim[j], at_offset);
1837 buffer[at_offset] = '\0';
1838 strcat(buffer, urls[i].text);
1839 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1840 end_offset = at_offset + strlen(urls[i].text);
1842 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1843 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1845 /* This assumes no templates start with the URL itself, and that they
1846 have at least two characters before the URL text */
1847 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1848 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1849 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1850 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1851 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1852 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1854 if (urls[i].is_url)
1856 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1857 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1858 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1859 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1860 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1861 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1863 else
1865 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1866 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1867 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1868 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1869 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1870 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1872 if (buffer[end_offset +1] != '\0')
1874 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1875 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1876 if (buffer[end_offset +2] != '\0')
1878 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1879 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1884 DestroyWindow(hwndRichEdit);
1885 hwndRichEdit = NULL;
1888 /* Test detection of URLs within normal text - WM_CHAR case. */
1889 /* Test only the first two URL examples for brevity */
1890 for (i = 0; i < 2; i++) {
1891 hwndRichEdit = new_richedit(parent);
1893 /* Also for brevity, test only the first three delimiters */
1894 for (j = 0; j < 3; j++) {
1895 char * at_pos;
1896 int at_offset;
1897 int end_offset;
1898 int u, v;
1900 at_pos = strchr(templates_delim[j], 'X');
1901 at_offset = at_pos - templates_delim[j];
1902 end_offset = at_offset + strlen(urls[i].text);
1904 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1905 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1906 for (u = 0; templates_delim[j][u]; u++) {
1907 if (templates_delim[j][u] == '\r') {
1908 simulate_typing_characters(hwndRichEdit, "\r");
1909 } else if (templates_delim[j][u] != 'X') {
1910 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1911 } else {
1912 for (v = 0; urls[i].text[v]; v++) {
1913 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1917 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1919 /* This assumes no templates start with the URL itself, and that they
1920 have at least two characters before the URL text */
1921 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1922 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1923 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1924 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1925 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1926 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1928 if (urls[i].is_url)
1930 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1931 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1932 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1933 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1935 else
1937 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1938 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1939 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1940 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1942 if (buffer[end_offset] != '\0')
1944 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1945 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1946 if (buffer[end_offset +1] != '\0')
1948 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1949 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1953 /* The following will insert a paragraph break after the first character
1954 of the URL candidate, thus breaking the URL. It is expected that the
1955 CFE_LINK attribute should break across both pieces of the URL */
1956 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1957 simulate_typing_characters(hwndRichEdit, "\r");
1958 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1960 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1961 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1962 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1963 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1964 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1965 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1967 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1968 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1969 /* end_offset moved because of paragraph break */
1970 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1971 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1972 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1973 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1975 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1976 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1977 if (buffer[end_offset +2] != '\0')
1979 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1980 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1984 /* The following will remove the just-inserted paragraph break, thus
1985 restoring the URL */
1986 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1987 simulate_typing_characters(hwndRichEdit, "\b");
1988 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1990 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1991 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1992 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1993 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1994 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1995 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1997 if (urls[i].is_url)
1999 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2000 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2001 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2002 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2004 else
2006 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2007 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2008 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2009 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2011 if (buffer[end_offset] != '\0')
2013 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2014 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2015 if (buffer[end_offset +1] != '\0')
2017 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2018 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2022 DestroyWindow(hwndRichEdit);
2023 hwndRichEdit = NULL;
2026 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2027 /* Test just the first two URL examples for brevity */
2028 for (i = 0; i < 2; i++) {
2029 SETTEXTEX st;
2031 hwndRichEdit = new_richedit(parent);
2033 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2034 be detected:
2035 1) Set entire text, a la WM_SETTEXT
2036 2) Set a selection of the text to the URL
2037 3) Set a portion of the text at a time, which eventually results in
2038 an URL
2039 All of them should give equivalent results
2042 /* Set entire text in one go, like WM_SETTEXT */
2043 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2044 char * at_pos;
2045 int at_offset;
2046 int end_offset;
2048 st.codepage = CP_ACP;
2049 st.flags = ST_DEFAULT;
2051 at_pos = strchr(templates_delim[j], 'X');
2052 at_offset = at_pos - templates_delim[j];
2053 strncpy(buffer, templates_delim[j], at_offset);
2054 buffer[at_offset] = '\0';
2055 strcat(buffer, urls[i].text);
2056 strcat(buffer, templates_delim[j] + at_offset + 1);
2057 end_offset = at_offset + strlen(urls[i].text);
2059 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2060 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2062 /* This assumes no templates start with the URL itself, and that they
2063 have at least two characters before the URL text */
2064 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2065 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2066 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2067 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2068 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2069 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2071 if (urls[i].is_url)
2073 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2074 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2075 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2076 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2078 else
2080 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2081 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2082 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2083 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2085 if (buffer[end_offset] != '\0')
2087 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2088 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2089 if (buffer[end_offset +1] != '\0')
2091 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2092 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2097 /* Set selection with X to the URL */
2098 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2099 char * at_pos;
2100 int at_offset;
2101 int end_offset;
2103 at_pos = strchr(templates_delim[j], 'X');
2104 at_offset = at_pos - templates_delim[j];
2105 end_offset = at_offset + strlen(urls[i].text);
2107 st.codepage = CP_ACP;
2108 st.flags = ST_DEFAULT;
2109 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2110 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2111 st.flags = ST_SELECTION;
2112 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2113 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2114 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2116 /* This assumes no templates start with the URL itself, and that they
2117 have at least two characters before the URL text */
2118 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2119 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2120 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2121 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2122 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2123 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2125 if (urls[i].is_url)
2127 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2128 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2129 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2130 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2132 else
2134 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2135 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2136 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2137 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2139 if (buffer[end_offset] != '\0')
2141 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2142 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2143 if (buffer[end_offset +1] != '\0')
2145 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2146 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2151 /* Set selection with X to the first character of the URL, then the rest */
2152 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2153 char * at_pos;
2154 int at_offset;
2155 int end_offset;
2157 at_pos = strchr(templates_delim[j], 'X');
2158 at_offset = at_pos - templates_delim[j];
2159 end_offset = at_offset + strlen(urls[i].text);
2161 strcpy(buffer, "YY");
2162 buffer[0] = urls[i].text[0];
2164 st.codepage = CP_ACP;
2165 st.flags = ST_DEFAULT;
2166 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2167 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2168 st.flags = ST_SELECTION;
2169 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2170 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2171 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2172 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2173 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2175 /* This assumes no templates start with the URL itself, and that they
2176 have at least two characters before the URL text */
2177 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2178 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2179 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2180 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2181 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2182 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2184 if (urls[i].is_url)
2186 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2187 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2188 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2189 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2191 else
2193 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2194 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2195 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2196 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2198 if (buffer[end_offset] != '\0')
2200 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2201 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2202 if (buffer[end_offset +1] != '\0')
2204 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2205 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2210 DestroyWindow(hwndRichEdit);
2211 hwndRichEdit = NULL;
2214 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2215 /* Test just the first two URL examples for brevity */
2216 for (i = 0; i < 2; i++) {
2217 hwndRichEdit = new_richedit(parent);
2219 /* Set selection with X to the URL */
2220 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2221 char * at_pos;
2222 int at_offset;
2223 int end_offset;
2225 at_pos = strchr(templates_delim[j], 'X');
2226 at_offset = at_pos - templates_delim[j];
2227 end_offset = at_offset + strlen(urls[i].text);
2229 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2230 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2231 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2232 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2233 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2235 /* This assumes no templates start with the URL itself, and that they
2236 have at least two characters before the URL text */
2237 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2238 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2239 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2240 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2241 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2242 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2244 if (urls[i].is_url)
2246 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2247 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2248 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2249 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2251 else
2253 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2254 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2255 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2256 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2258 if (buffer[end_offset] != '\0')
2260 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2261 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2262 if (buffer[end_offset +1] != '\0')
2264 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2265 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2270 /* Set selection with X to the first character of the URL, then the rest */
2271 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2272 char * at_pos;
2273 int at_offset;
2274 int end_offset;
2276 at_pos = strchr(templates_delim[j], 'X');
2277 at_offset = at_pos - templates_delim[j];
2278 end_offset = at_offset + strlen(urls[i].text);
2280 strcpy(buffer, "YY");
2281 buffer[0] = urls[i].text[0];
2283 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2284 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2285 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2286 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2287 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2288 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2289 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2291 /* This assumes no templates start with the URL itself, and that they
2292 have at least two characters before the URL text */
2293 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2294 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2295 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2296 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2297 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2298 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2300 if (urls[i].is_url)
2302 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2303 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2304 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2305 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2307 else
2309 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2310 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2311 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2312 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2314 if (buffer[end_offset] != '\0')
2316 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2317 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2318 if (buffer[end_offset +1] != '\0')
2320 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2321 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2326 DestroyWindow(hwndRichEdit);
2327 hwndRichEdit = NULL;
2330 DestroyWindow(parent);
2333 static void test_EM_SCROLL(void)
2335 int i, j;
2336 int r; /* return value */
2337 int expr; /* expected return value */
2338 HWND hwndRichEdit = new_richedit(NULL);
2339 int y_before, y_after; /* units of lines of text */
2341 /* test a richedit box containing a single line of text */
2342 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2343 expr = 0x00010000;
2344 for (i = 0; i < 4; i++) {
2345 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2347 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2348 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2349 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2350 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2351 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2352 "(i == %d)\n", y_after, i);
2356 * test a richedit box that will scroll. There are two general
2357 * cases: the case without any long lines and the case with a long
2358 * line.
2360 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2361 if (i == 0)
2362 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2363 else
2364 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2365 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2366 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2367 "LONG LINE \nb\nc\nd\ne");
2368 for (j = 0; j < 12; j++) /* reset scroll position to top */
2369 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2371 /* get first visible line */
2372 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2373 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2375 /* get new current first visible line */
2376 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2378 ok(((r & 0xffffff00) == 0x00010000) &&
2379 ((r & 0x000000ff) != 0x00000000),
2380 "EM_SCROLL page down didn't scroll by a small positive number of "
2381 "lines (r == 0x%08x)\n", r);
2382 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2383 "(line %d scrolled to line %d\n", y_before, y_after);
2385 y_before = y_after;
2387 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2388 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2389 ok(((r & 0xffffff00) == 0x0001ff00),
2390 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2391 "(r == 0x%08x)\n", r);
2392 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2393 "%d scrolled to line %d\n", y_before, y_after);
2395 y_before = y_after;
2397 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2399 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2401 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2402 "(r == 0x%08x)\n", r);
2403 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2404 "1 line (%d scrolled to %d)\n", y_before, y_after);
2406 y_before = y_after;
2408 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2410 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2412 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2413 "(r == 0x%08x)\n", r);
2414 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2415 "line (%d scrolled to %d)\n", y_before, y_after);
2417 y_before = y_after;
2419 r = SendMessage(hwndRichEdit, EM_SCROLL,
2420 SB_LINEUP, 0); /* lineup beyond top */
2422 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2424 ok(r == 0x00010000,
2425 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2426 ok(y_before == y_after,
2427 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2429 y_before = y_after;
2431 r = SendMessage(hwndRichEdit, EM_SCROLL,
2432 SB_PAGEUP, 0);/*page up beyond top */
2434 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2436 ok(r == 0x00010000,
2437 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2438 ok(y_before == y_after,
2439 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2441 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2442 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2443 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2444 r = SendMessage(hwndRichEdit, EM_SCROLL,
2445 SB_PAGEDOWN, 0); /* page down beyond bot */
2446 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2448 ok(r == 0x00010000,
2449 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2450 ok(y_before == y_after,
2451 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2452 y_before, y_after);
2454 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2455 SendMessage(hwndRichEdit, EM_SCROLL,
2456 SB_LINEDOWN, 0); /* line down beyond bot */
2457 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2459 ok(r == 0x00010000,
2460 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2461 ok(y_before == y_after,
2462 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2463 y_before, y_after);
2465 DestroyWindow(hwndRichEdit);
2468 static unsigned int recursionLevel = 0;
2469 static unsigned int WM_SIZE_recursionLevel = 0;
2470 static BOOL bailedOutOfRecursion = FALSE;
2471 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2473 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2475 LRESULT r;
2477 if (bailedOutOfRecursion) return 0;
2478 if (recursionLevel >= 32) {
2479 bailedOutOfRecursion = TRUE;
2480 return 0;
2483 recursionLevel++;
2484 switch (message) {
2485 case WM_SIZE:
2486 WM_SIZE_recursionLevel++;
2487 r = richeditProc(hwnd, message, wParam, lParam);
2488 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2489 ShowScrollBar(hwnd, SB_VERT, TRUE);
2490 WM_SIZE_recursionLevel--;
2491 break;
2492 default:
2493 r = richeditProc(hwnd, message, wParam, lParam);
2494 break;
2496 recursionLevel--;
2497 return r;
2500 static void test_scrollbar_visibility(void)
2502 HWND hwndRichEdit;
2503 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2504 SCROLLINFO si;
2505 WNDCLASSA cls;
2506 BOOL r;
2508 /* These tests show that richedit should temporarily refrain from automatically
2509 hiding or showing its scrollbars (vertical at least) when an explicit request
2510 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2511 Some applications depend on forced showing (when otherwise richedit would
2512 hide the vertical scrollbar) and are thrown on an endless recursive loop
2513 if richedit auto-hides the scrollbar again. Apparently they never heard of
2514 the ES_DISABLENOSCROLL style... */
2516 hwndRichEdit = new_richedit(NULL);
2518 /* Test default scrollbar visibility behavior */
2519 memset(&si, 0, sizeof(si));
2520 si.cbSize = sizeof(si);
2521 si.fMask = SIF_PAGE | SIF_RANGE;
2522 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2523 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2524 "Vertical scrollbar is visible, should be invisible.\n");
2525 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2526 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2527 si.nPage, si.nMin, si.nMax);
2529 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2530 memset(&si, 0, sizeof(si));
2531 si.cbSize = sizeof(si);
2532 si.fMask = SIF_PAGE | SIF_RANGE;
2533 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2534 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2535 "Vertical scrollbar is visible, should be invisible.\n");
2536 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2537 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2538 si.nPage, si.nMin, si.nMax);
2540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2541 memset(&si, 0, sizeof(si));
2542 si.cbSize = sizeof(si);
2543 si.fMask = SIF_PAGE | SIF_RANGE;
2544 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2545 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2546 "Vertical scrollbar is invisible, should be visible.\n");
2547 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2548 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2549 si.nPage, si.nMin, si.nMax);
2551 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2552 even though it hides the scrollbar */
2553 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2554 memset(&si, 0, sizeof(si));
2555 si.cbSize = sizeof(si);
2556 si.fMask = SIF_PAGE | SIF_RANGE;
2557 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2558 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2559 "Vertical scrollbar is visible, should be invisible.\n");
2560 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2561 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2562 si.nPage, si.nMin, si.nMax);
2564 /* Setting non-scrolling text again does *not* reset scrollbar range */
2565 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2566 memset(&si, 0, sizeof(si));
2567 si.cbSize = sizeof(si);
2568 si.fMask = SIF_PAGE | SIF_RANGE;
2569 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2570 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2571 "Vertical scrollbar is visible, should be invisible.\n");
2572 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2573 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2574 si.nPage, si.nMin, si.nMax);
2576 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2577 memset(&si, 0, sizeof(si));
2578 si.cbSize = sizeof(si);
2579 si.fMask = SIF_PAGE | SIF_RANGE;
2580 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2581 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2582 "Vertical scrollbar is visible, should be invisible.\n");
2583 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2584 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2585 si.nPage, si.nMin, si.nMax);
2587 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2588 memset(&si, 0, sizeof(si));
2589 si.cbSize = sizeof(si);
2590 si.fMask = SIF_PAGE | SIF_RANGE;
2591 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2592 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2593 "Vertical scrollbar is visible, should be invisible.\n");
2594 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2595 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2596 si.nPage, si.nMin, si.nMax);
2598 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2599 memset(&si, 0, sizeof(si));
2600 si.cbSize = sizeof(si);
2601 si.fMask = SIF_PAGE | SIF_RANGE;
2602 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2603 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2604 "Vertical scrollbar is visible, should be invisible.\n");
2605 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2606 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2607 si.nPage, si.nMin, si.nMax);
2609 DestroyWindow(hwndRichEdit);
2611 /* Test again, with ES_DISABLENOSCROLL style */
2612 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2614 /* Test default scrollbar visibility behavior */
2615 memset(&si, 0, sizeof(si));
2616 si.cbSize = sizeof(si);
2617 si.fMask = SIF_PAGE | SIF_RANGE;
2618 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2619 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2620 "Vertical scrollbar is invisible, should be visible.\n");
2621 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2622 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2623 si.nPage, si.nMin, si.nMax);
2625 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2626 memset(&si, 0, sizeof(si));
2627 si.cbSize = sizeof(si);
2628 si.fMask = SIF_PAGE | SIF_RANGE;
2629 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2630 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2631 "Vertical scrollbar is invisible, should be visible.\n");
2632 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2633 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2634 si.nPage, si.nMin, si.nMax);
2636 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2637 memset(&si, 0, sizeof(si));
2638 si.cbSize = sizeof(si);
2639 si.fMask = SIF_PAGE | SIF_RANGE;
2640 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2641 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2642 "Vertical scrollbar is invisible, should be visible.\n");
2643 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2644 "reported page/range is %d (%d..%d)\n",
2645 si.nPage, si.nMin, si.nMax);
2647 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2648 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2649 memset(&si, 0, sizeof(si));
2650 si.cbSize = sizeof(si);
2651 si.fMask = SIF_PAGE | SIF_RANGE;
2652 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2653 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2654 "Vertical scrollbar is invisible, should be visible.\n");
2655 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2656 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2657 si.nPage, si.nMin, si.nMax);
2659 /* Setting non-scrolling text again does *not* reset scrollbar range */
2660 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2661 memset(&si, 0, sizeof(si));
2662 si.cbSize = sizeof(si);
2663 si.fMask = SIF_PAGE | SIF_RANGE;
2664 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2665 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2666 "Vertical scrollbar is invisible, should be visible.\n");
2667 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2668 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2669 si.nPage, si.nMin, si.nMax);
2671 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2672 memset(&si, 0, sizeof(si));
2673 si.cbSize = sizeof(si);
2674 si.fMask = SIF_PAGE | SIF_RANGE;
2675 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2676 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2677 "Vertical scrollbar is invisible, should be visible.\n");
2678 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2679 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2680 si.nPage, si.nMin, si.nMax);
2682 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2683 memset(&si, 0, sizeof(si));
2684 si.cbSize = sizeof(si);
2685 si.fMask = SIF_PAGE | SIF_RANGE;
2686 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2687 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2688 "Vertical scrollbar is invisible, should be visible.\n");
2689 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2690 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2691 si.nPage, si.nMin, si.nMax);
2693 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2694 memset(&si, 0, sizeof(si));
2695 si.cbSize = sizeof(si);
2696 si.fMask = SIF_PAGE | SIF_RANGE;
2697 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2698 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2699 "Vertical scrollbar is invisible, should be visible.\n");
2700 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2701 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2702 si.nPage, si.nMin, si.nMax);
2704 DestroyWindow(hwndRichEdit);
2706 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2707 hwndRichEdit = new_richedit(NULL);
2709 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2710 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2711 memset(&si, 0, sizeof(si));
2712 si.cbSize = sizeof(si);
2713 si.fMask = SIF_PAGE | SIF_RANGE;
2714 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2715 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2716 "Vertical scrollbar is invisible, should be visible.\n");
2717 todo_wine {
2718 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2719 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2720 si.nPage, si.nMin, si.nMax);
2723 /* Ditto, see above */
2724 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2725 memset(&si, 0, sizeof(si));
2726 si.cbSize = sizeof(si);
2727 si.fMask = SIF_PAGE | SIF_RANGE;
2728 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2729 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2730 "Vertical scrollbar is invisible, should be visible.\n");
2731 todo_wine {
2732 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2733 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2734 si.nPage, si.nMin, si.nMax);
2737 /* Ditto, see above */
2738 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2739 memset(&si, 0, sizeof(si));
2740 si.cbSize = sizeof(si);
2741 si.fMask = SIF_PAGE | SIF_RANGE;
2742 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2743 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2744 "Vertical scrollbar is invisible, should be visible.\n");
2745 todo_wine {
2746 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2747 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2748 si.nPage, si.nMin, si.nMax);
2751 /* Ditto, see above */
2752 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2753 memset(&si, 0, sizeof(si));
2754 si.cbSize = sizeof(si);
2755 si.fMask = SIF_PAGE | SIF_RANGE;
2756 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2757 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2758 "Vertical scrollbar is invisible, should be visible.\n");
2759 todo_wine {
2760 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2761 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2762 si.nPage, si.nMin, si.nMax);
2765 /* Ditto, see above */
2766 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2767 memset(&si, 0, sizeof(si));
2768 si.cbSize = sizeof(si);
2769 si.fMask = SIF_PAGE | SIF_RANGE;
2770 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2771 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2772 "Vertical scrollbar is invisible, should be visible.\n");
2773 todo_wine {
2774 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2775 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2776 si.nPage, si.nMin, si.nMax);
2779 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2780 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2781 memset(&si, 0, sizeof(si));
2782 si.cbSize = sizeof(si);
2783 si.fMask = SIF_PAGE | SIF_RANGE;
2784 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2785 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2786 "Vertical scrollbar is visible, should be invisible.\n");
2787 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2788 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2789 si.nPage, si.nMin, si.nMax);
2791 DestroyWindow(hwndRichEdit);
2793 hwndRichEdit = new_richedit(NULL);
2795 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2796 memset(&si, 0, sizeof(si));
2797 si.cbSize = sizeof(si);
2798 si.fMask = SIF_PAGE | SIF_RANGE;
2799 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2800 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2801 "Vertical scrollbar is visible, should be invisible.\n");
2802 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2803 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2804 si.nPage, si.nMin, si.nMax);
2806 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2807 memset(&si, 0, sizeof(si));
2808 si.cbSize = sizeof(si);
2809 si.fMask = SIF_PAGE | SIF_RANGE;
2810 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2811 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2812 "Vertical scrollbar is visible, should be invisible.\n");
2813 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2814 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2815 si.nPage, si.nMin, si.nMax);
2817 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2818 memset(&si, 0, sizeof(si));
2819 si.cbSize = sizeof(si);
2820 si.fMask = SIF_PAGE | SIF_RANGE;
2821 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2822 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2823 "Vertical scrollbar is visible, should be invisible.\n");
2824 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2825 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2826 si.nPage, si.nMin, si.nMax);
2828 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2829 memset(&si, 0, sizeof(si));
2830 si.cbSize = sizeof(si);
2831 si.fMask = SIF_PAGE | SIF_RANGE;
2832 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2833 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2834 "Vertical scrollbar is visible, should be invisible.\n");
2835 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2836 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2837 si.nPage, si.nMin, si.nMax);
2839 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2840 memset(&si, 0, sizeof(si));
2841 si.cbSize = sizeof(si);
2842 si.fMask = SIF_PAGE | SIF_RANGE;
2843 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2844 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2845 "Vertical scrollbar is invisible, should be visible.\n");
2846 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2847 "reported page/range is %d (%d..%d)\n",
2848 si.nPage, si.nMin, si.nMax);
2850 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2851 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2852 memset(&si, 0, sizeof(si));
2853 si.cbSize = sizeof(si);
2854 si.fMask = SIF_PAGE | SIF_RANGE;
2855 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2856 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2857 "Vertical scrollbar is visible, should be invisible.\n");
2858 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2859 "reported page/range is %d (%d..%d)\n",
2860 si.nPage, si.nMin, si.nMax);
2862 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2863 memset(&si, 0, sizeof(si));
2864 si.cbSize = sizeof(si);
2865 si.fMask = SIF_PAGE | SIF_RANGE;
2866 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2867 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2868 "Vertical scrollbar is visible, should be invisible.\n");
2869 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2870 "reported page/range is %d (%d..%d)\n",
2871 si.nPage, si.nMin, si.nMax);
2873 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2874 EM_SCROLL will make visible any forcefully invisible scrollbar */
2875 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2876 memset(&si, 0, sizeof(si));
2877 si.cbSize = sizeof(si);
2878 si.fMask = SIF_PAGE | SIF_RANGE;
2879 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2880 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2881 "Vertical scrollbar is invisible, should be visible.\n");
2882 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2883 "reported page/range is %d (%d..%d)\n",
2884 si.nPage, si.nMin, si.nMax);
2886 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2887 memset(&si, 0, sizeof(si));
2888 si.cbSize = sizeof(si);
2889 si.fMask = SIF_PAGE | SIF_RANGE;
2890 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2891 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2892 "Vertical scrollbar is visible, should be invisible.\n");
2893 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2894 "reported page/range is %d (%d..%d)\n",
2895 si.nPage, si.nMin, si.nMax);
2897 /* Again, EM_SCROLL, with SB_LINEUP */
2898 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2899 memset(&si, 0, sizeof(si));
2900 si.cbSize = sizeof(si);
2901 si.fMask = SIF_PAGE | SIF_RANGE;
2902 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2903 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2904 "Vertical scrollbar is invisible, should be visible.\n");
2905 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2906 "reported page/range is %d (%d..%d)\n",
2907 si.nPage, si.nMin, si.nMax);
2909 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2910 memset(&si, 0, sizeof(si));
2911 si.cbSize = sizeof(si);
2912 si.fMask = SIF_PAGE | SIF_RANGE;
2913 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2914 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2915 "Vertical scrollbar is visible, should be invisible.\n");
2916 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2917 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2918 si.nPage, si.nMin, si.nMax);
2920 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2921 memset(&si, 0, sizeof(si));
2922 si.cbSize = sizeof(si);
2923 si.fMask = SIF_PAGE | SIF_RANGE;
2924 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2925 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2926 "Vertical scrollbar is invisible, should be visible.\n");
2927 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2928 "reported page/range is %d (%d..%d)\n",
2929 si.nPage, si.nMin, si.nMax);
2931 DestroyWindow(hwndRichEdit);
2934 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2935 hwndRichEdit = new_richedit(NULL);
2937 #define ENABLE_WS_VSCROLL(hwnd) \
2938 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2939 #define DISABLE_WS_VSCROLL(hwnd) \
2940 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2942 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2943 ENABLE_WS_VSCROLL(hwndRichEdit);
2944 memset(&si, 0, sizeof(si));
2945 si.cbSize = sizeof(si);
2946 si.fMask = SIF_PAGE | SIF_RANGE;
2947 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2948 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2949 "Vertical scrollbar is invisible, should be visible.\n");
2950 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2951 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2952 si.nPage, si.nMin, si.nMax);
2954 /* Ditto, see above */
2955 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2956 memset(&si, 0, sizeof(si));
2957 si.cbSize = sizeof(si);
2958 si.fMask = SIF_PAGE | SIF_RANGE;
2959 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2960 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2961 "Vertical scrollbar is invisible, should be visible.\n");
2962 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2963 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2964 si.nPage, si.nMin, si.nMax);
2966 /* Ditto, see above */
2967 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2968 memset(&si, 0, sizeof(si));
2969 si.cbSize = sizeof(si);
2970 si.fMask = SIF_PAGE | SIF_RANGE;
2971 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2972 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2973 "Vertical scrollbar is invisible, should be visible.\n");
2974 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2975 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2976 si.nPage, si.nMin, si.nMax);
2978 /* Ditto, see above */
2979 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2980 memset(&si, 0, sizeof(si));
2981 si.cbSize = sizeof(si);
2982 si.fMask = SIF_PAGE | SIF_RANGE;
2983 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2984 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2985 "Vertical scrollbar is invisible, should be visible.\n");
2986 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2987 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2988 si.nPage, si.nMin, si.nMax);
2990 /* Ditto, see above */
2991 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2992 memset(&si, 0, sizeof(si));
2993 si.cbSize = sizeof(si);
2994 si.fMask = SIF_PAGE | SIF_RANGE;
2995 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2996 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2997 "Vertical scrollbar is invisible, should be visible.\n");
2998 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2999 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3000 si.nPage, si.nMin, si.nMax);
3002 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3003 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3004 memset(&si, 0, sizeof(si));
3005 si.cbSize = sizeof(si);
3006 si.fMask = SIF_PAGE | SIF_RANGE;
3007 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3008 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3009 "Vertical scrollbar is visible, should be invisible.\n");
3010 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3011 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3012 si.nPage, si.nMin, si.nMax);
3014 DestroyWindow(hwndRichEdit);
3016 hwndRichEdit = new_richedit(NULL);
3018 DISABLE_WS_VSCROLL(hwndRichEdit);
3019 memset(&si, 0, sizeof(si));
3020 si.cbSize = sizeof(si);
3021 si.fMask = SIF_PAGE | SIF_RANGE;
3022 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3023 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3024 "Vertical scrollbar is visible, should be invisible.\n");
3025 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3026 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3027 si.nPage, si.nMin, si.nMax);
3029 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3030 memset(&si, 0, sizeof(si));
3031 si.cbSize = sizeof(si);
3032 si.fMask = SIF_PAGE | SIF_RANGE;
3033 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3034 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3035 "Vertical scrollbar is visible, should be invisible.\n");
3036 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3037 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3038 si.nPage, si.nMin, si.nMax);
3040 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3041 memset(&si, 0, sizeof(si));
3042 si.cbSize = sizeof(si);
3043 si.fMask = SIF_PAGE | SIF_RANGE;
3044 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3045 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3046 "Vertical scrollbar is visible, should be invisible.\n");
3047 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3048 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3049 si.nPage, si.nMin, si.nMax);
3051 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3052 memset(&si, 0, sizeof(si));
3053 si.cbSize = sizeof(si);
3054 si.fMask = SIF_PAGE | SIF_RANGE;
3055 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3056 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3057 "Vertical scrollbar is visible, should be invisible.\n");
3058 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3059 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3060 si.nPage, si.nMin, si.nMax);
3062 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3063 memset(&si, 0, sizeof(si));
3064 si.cbSize = sizeof(si);
3065 si.fMask = SIF_PAGE | SIF_RANGE;
3066 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3067 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3068 "Vertical scrollbar is invisible, should be visible.\n");
3069 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3070 "reported page/range is %d (%d..%d)\n",
3071 si.nPage, si.nMin, si.nMax);
3073 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3074 DISABLE_WS_VSCROLL(hwndRichEdit);
3075 memset(&si, 0, sizeof(si));
3076 si.cbSize = sizeof(si);
3077 si.fMask = SIF_PAGE | SIF_RANGE;
3078 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3079 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3080 "Vertical scrollbar is visible, should be invisible.\n");
3081 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3082 "reported page/range is %d (%d..%d)\n",
3083 si.nPage, si.nMin, si.nMax);
3085 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3086 memset(&si, 0, sizeof(si));
3087 si.cbSize = sizeof(si);
3088 si.fMask = SIF_PAGE | SIF_RANGE;
3089 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3090 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3091 "Vertical scrollbar is visible, should be invisible.\n");
3092 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3093 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3094 si.nPage, si.nMin, si.nMax);
3096 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3097 memset(&si, 0, sizeof(si));
3098 si.cbSize = sizeof(si);
3099 si.fMask = SIF_PAGE | SIF_RANGE;
3100 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3101 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3102 "Vertical scrollbar is invisible, should be visible.\n");
3103 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3104 "reported page/range is %d (%d..%d)\n",
3105 si.nPage, si.nMin, si.nMax);
3107 DISABLE_WS_VSCROLL(hwndRichEdit);
3108 memset(&si, 0, sizeof(si));
3109 si.cbSize = sizeof(si);
3110 si.fMask = SIF_PAGE | SIF_RANGE;
3111 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3112 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3113 "Vertical scrollbar is visible, should be invisible.\n");
3114 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3115 "reported page/range is %d (%d..%d)\n",
3116 si.nPage, si.nMin, si.nMax);
3118 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3119 EM_SCROLL will make visible any forcefully invisible scrollbar */
3120 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3121 memset(&si, 0, sizeof(si));
3122 si.cbSize = sizeof(si);
3123 si.fMask = SIF_PAGE | SIF_RANGE;
3124 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3125 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3126 "Vertical scrollbar is invisible, should be visible.\n");
3127 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3128 "reported page/range is %d (%d..%d)\n",
3129 si.nPage, si.nMin, si.nMax);
3131 DISABLE_WS_VSCROLL(hwndRichEdit);
3132 memset(&si, 0, sizeof(si));
3133 si.cbSize = sizeof(si);
3134 si.fMask = SIF_PAGE | SIF_RANGE;
3135 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3136 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3137 "Vertical scrollbar is visible, should be invisible.\n");
3138 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3139 "reported page/range is %d (%d..%d)\n",
3140 si.nPage, si.nMin, si.nMax);
3142 /* Again, EM_SCROLL, with SB_LINEUP */
3143 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3144 memset(&si, 0, sizeof(si));
3145 si.cbSize = sizeof(si);
3146 si.fMask = SIF_PAGE | SIF_RANGE;
3147 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3148 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3149 "Vertical scrollbar is invisible, should be visible.\n");
3150 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3151 "reported page/range is %d (%d..%d)\n",
3152 si.nPage, si.nMin, si.nMax);
3154 DestroyWindow(hwndRichEdit);
3156 /* This window proc models what is going on with Corman Lisp 3.0.
3157 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3158 force the scrollbar into visibility. Recursion should NOT happen
3159 as a result of this action.
3161 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3162 if (r) {
3163 richeditProc = cls.lpfnWndProc;
3164 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3165 cls.lpszClassName = "RicheditStupidOverride";
3166 if(!RegisterClassA(&cls)) assert(0);
3168 recursionLevel = 0;
3169 WM_SIZE_recursionLevel = 0;
3170 bailedOutOfRecursion = FALSE;
3171 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3172 ok(!bailedOutOfRecursion,
3173 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3175 recursionLevel = 0;
3176 WM_SIZE_recursionLevel = 0;
3177 bailedOutOfRecursion = FALSE;
3178 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3179 ok(!bailedOutOfRecursion,
3180 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3182 /* Unblock window in order to process WM_DESTROY */
3183 recursionLevel = 0;
3184 bailedOutOfRecursion = FALSE;
3185 WM_SIZE_recursionLevel = 0;
3186 DestroyWindow(hwndRichEdit);
3190 static void test_EM_SETUNDOLIMIT(void)
3192 /* cases we test for:
3193 * default behaviour - limiting at 100 undo's
3194 * undo disabled - setting a limit of 0
3195 * undo limited - undo limit set to some to some number, like 2
3196 * bad input - sending a negative number should default to 100 undo's */
3198 HWND hwndRichEdit = new_richedit(NULL);
3199 CHARRANGE cr;
3200 int i;
3201 int result;
3203 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3204 cr.cpMin = 0;
3205 cr.cpMax = 1;
3206 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3207 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3208 also, multiple pastes don't combine like WM_CHAR would */
3209 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3211 /* first case - check the default */
3212 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3213 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3214 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3215 for (i=0; i<100; i++) /* Undo 100 of them */
3216 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3217 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3218 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3220 /* second case - cannot undo */
3221 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3222 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3223 SendMessage(hwndRichEdit,
3224 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3225 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3226 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3228 /* third case - set it to an arbitrary number */
3229 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3230 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3231 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3232 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3233 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3234 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3235 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3236 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3237 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3238 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3239 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3240 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3241 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3242 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3244 /* fourth case - setting negative numbers should default to 100 undos */
3245 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3246 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3247 ok (result == 100,
3248 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3250 DestroyWindow(hwndRichEdit);
3253 static void test_ES_PASSWORD(void)
3255 /* This isn't hugely testable, so we're just going to run it through its paces */
3257 HWND hwndRichEdit = new_richedit(NULL);
3258 WCHAR result;
3260 /* First, check the default of a regular control */
3261 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3262 ok (result == 0,
3263 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3265 /* Now, set it to something normal */
3266 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3267 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3268 ok (result == 120,
3269 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3271 /* Now, set it to something odd */
3272 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3273 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3274 ok (result == 1234,
3275 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3276 DestroyWindow(hwndRichEdit);
3279 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3280 LPBYTE pbBuff,
3281 LONG cb,
3282 LONG *pcb)
3284 char** str = (char**)dwCookie;
3285 *pcb = cb;
3286 if (*pcb > 0) {
3287 memcpy(*str, pbBuff, *pcb);
3288 *str += *pcb;
3290 return 0;
3293 static void test_WM_SETTEXT(void)
3295 HWND hwndRichEdit = new_richedit(NULL);
3296 const char * TestItem1 = "TestSomeText";
3297 const char * TestItem2 = "TestSomeText\r";
3298 const char * TestItem2_after = "TestSomeText\r\n";
3299 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3300 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3301 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3302 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3303 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3304 const char * TestItem5_after = "TestSomeText TestSomeText";
3305 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3306 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3307 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3308 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3310 const char rtftextA[] = "{\\rtf sometext}";
3311 const char urtftextA[] = "{\\urtf sometext}";
3312 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3313 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3314 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3316 char buf[1024] = {0};
3317 WCHAR bufW[1024] = {0};
3318 LRESULT result;
3320 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3321 any solitary \r to be converted to \r\n on return. Properly paired
3322 \r\n are not affected. It also shows that the special sequence \r\r\n
3323 gets converted to a single space.
3326 #define TEST_SETTEXT(a, b) \
3327 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3328 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3329 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3330 ok (result == lstrlen(buf), \
3331 "WM_GETTEXT returned %ld instead of expected %u\n", \
3332 result, lstrlen(buf)); \
3333 result = strcmp(b, buf); \
3334 ok(result == 0, \
3335 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3337 TEST_SETTEXT(TestItem1, TestItem1)
3338 TEST_SETTEXT(TestItem2, TestItem2_after)
3339 TEST_SETTEXT(TestItem3, TestItem3_after)
3340 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3341 TEST_SETTEXT(TestItem4, TestItem4_after)
3342 TEST_SETTEXT(TestItem5, TestItem5_after)
3343 TEST_SETTEXT(TestItem6, TestItem6_after)
3344 TEST_SETTEXT(TestItem7, TestItem7_after)
3346 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3347 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3348 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3349 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3350 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3351 DestroyWindow(hwndRichEdit);
3352 #undef TEST_SETTEXT
3354 #define TEST_SETTEXTW(a, b) \
3355 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3356 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3357 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3358 ok (result == lstrlenW(bufW), \
3359 "WM_GETTEXT returned %ld instead of expected %u\n", \
3360 result, lstrlenW(bufW)); \
3361 result = lstrcmpW(b, bufW); \
3362 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3364 if (is_win9x)
3366 skip("Cannot perform unicode tests\n");
3367 return;
3369 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3370 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3371 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3372 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3373 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3374 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3375 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3376 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3377 DestroyWindow(hwndRichEdit);
3378 #undef TEST_SETTEXTW
3381 static void test_EM_STREAMOUT(void)
3383 HWND hwndRichEdit = new_richedit(NULL);
3384 int r;
3385 EDITSTREAM es;
3386 char buf[1024] = {0};
3387 char * p;
3389 const char * TestItem1 = "TestSomeText";
3390 const char * TestItem2 = "TestSomeText\r";
3391 const char * TestItem3 = "TestSomeText\r\n";
3393 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3394 p = buf;
3395 es.dwCookie = (DWORD_PTR)&p;
3396 es.dwError = 0;
3397 es.pfnCallback = test_WM_SETTEXT_esCallback;
3398 memset(buf, 0, sizeof(buf));
3399 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3400 r = strlen(buf);
3401 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3402 ok(strcmp(buf, TestItem1) == 0,
3403 "streamed text different, got %s\n", buf);
3405 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3406 p = buf;
3407 es.dwCookie = (DWORD_PTR)&p;
3408 es.dwError = 0;
3409 es.pfnCallback = test_WM_SETTEXT_esCallback;
3410 memset(buf, 0, sizeof(buf));
3411 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3412 r = strlen(buf);
3413 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3414 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3415 ok(strcmp(buf, TestItem3) == 0,
3416 "streamed text different from, got %s\n", buf);
3417 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3418 p = buf;
3419 es.dwCookie = (DWORD_PTR)&p;
3420 es.dwError = 0;
3421 es.pfnCallback = test_WM_SETTEXT_esCallback;
3422 memset(buf, 0, sizeof(buf));
3423 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3424 r = strlen(buf);
3425 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3426 ok(strcmp(buf, TestItem3) == 0,
3427 "streamed text different, got %s\n", buf);
3429 DestroyWindow(hwndRichEdit);
3432 static void test_EM_STREAMOUT_FONTTBL(void)
3434 HWND hwndRichEdit = new_richedit(NULL);
3435 EDITSTREAM es;
3436 char buf[1024] = {0};
3437 char * p;
3438 char * fontTbl;
3439 int brackCount;
3441 const char * TestItem = "TestSomeText";
3443 /* fills in the richedit control with some text */
3444 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3446 /* streams out the text in rtf format */
3447 p = buf;
3448 es.dwCookie = (DWORD_PTR)&p;
3449 es.dwError = 0;
3450 es.pfnCallback = test_WM_SETTEXT_esCallback;
3451 memset(buf, 0, sizeof(buf));
3452 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3454 /* scans for \fonttbl, error if not found */
3455 fontTbl = strstr(buf, "\\fonttbl");
3456 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3457 if(fontTbl)
3459 /* scans for terminating closing bracket */
3460 brackCount = 1;
3461 while(*fontTbl && brackCount)
3463 if(*fontTbl == '{')
3464 brackCount++;
3465 else if(*fontTbl == '}')
3466 brackCount--;
3467 fontTbl++;
3469 /* checks whether closing bracket is ok */
3470 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3471 if(!brackCount)
3473 /* char before closing fonttbl block should be a closed bracket */
3474 fontTbl -= 2;
3475 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3477 /* char after fonttbl block should be a crlf */
3478 fontTbl += 2;
3479 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3482 DestroyWindow(hwndRichEdit);
3486 static void test_EM_SETTEXTEX(void)
3488 HWND hwndRichEdit, parent;
3489 SCROLLINFO si;
3490 int sel_start, sel_end;
3491 SETTEXTEX setText;
3492 GETTEXTEX getText;
3493 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3494 'S', 'o', 'm', 'e',
3495 'T', 'e', 'x', 't', 0};
3496 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3497 't', 'S', 'o', 'm',
3498 'e', 'T', 'e', 'x',
3499 't', 't', 'S', 'o',
3500 'm', 'e', 'T', 'e',
3501 'x', 't', 0};
3502 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3503 '\r','t','S','o','m','e','T','e','x','t',0};
3504 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3505 'S', 'o', 'm', 'e',
3506 'T', 'e', 'x', 't',
3507 '\r', 0};
3508 const char * TestItem2_after = "TestSomeText\r\n";
3509 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3510 'S', 'o', 'm', 'e',
3511 'T', 'e', 'x', 't',
3512 '\r','\n','\r','\n', 0};
3513 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3514 'S', 'o', 'm', 'e',
3515 'T', 'e', 'x', 't',
3516 '\n','\n', 0};
3517 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3518 'S', 'o', 'm', 'e',
3519 'T', 'e', 'x', 't',
3520 '\r','\r', 0};
3521 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3522 'S', 'o', 'm', 'e',
3523 'T', 'e', 'x', 't',
3524 '\r','\r','\n','\r',
3525 '\n', 0};
3526 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3527 'S', 'o', 'm', 'e',
3528 'T', 'e', 'x', 't',
3529 ' ','\r', 0};
3530 #define MAX_BUF_LEN 1024
3531 WCHAR buf[MAX_BUF_LEN];
3532 char bufACP[MAX_BUF_LEN];
3533 char * p;
3534 int result;
3535 CHARRANGE cr;
3536 EDITSTREAM es;
3537 WNDCLASSA cls;
3539 /* Test the scroll position with and without a parent window.
3541 * For some reason the scroll position is 0 after EM_SETTEXTEX
3542 * with the ST_SELECTION flag only when the control has a parent
3543 * window, even though the selection is at the end. */
3544 cls.style = 0;
3545 cls.lpfnWndProc = DefWindowProcA;
3546 cls.cbClsExtra = 0;
3547 cls.cbWndExtra = 0;
3548 cls.hInstance = GetModuleHandleA(0);
3549 cls.hIcon = 0;
3550 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3551 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3552 cls.lpszMenuName = NULL;
3553 cls.lpszClassName = "ParentTestClass";
3554 if(!RegisterClassA(&cls)) assert(0);
3556 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3557 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3558 ok (parent != 0, "Failed to create parent window\n");
3560 hwndRichEdit = CreateWindowEx(0,
3561 RICHEDIT_CLASS, NULL,
3562 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3563 0, 0, 200, 60, parent, NULL,
3564 hmoduleRichEdit, NULL);
3566 setText.codepage = CP_ACP;
3567 setText.flags = ST_SELECTION;
3568 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3569 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3570 si.cbSize = sizeof(si);
3571 si.fMask = SIF_ALL;
3572 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3573 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3574 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3575 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3576 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3578 DestroyWindow(parent);
3580 /* Test without a parent window */
3581 hwndRichEdit = new_richedit(NULL);
3582 setText.codepage = CP_ACP;
3583 setText.flags = ST_SELECTION;
3584 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3585 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3586 si.cbSize = sizeof(si);
3587 si.fMask = SIF_ALL;
3588 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3589 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3590 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3591 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3592 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3594 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3595 * but this time it is because the selection is at the beginning. */
3596 setText.codepage = CP_ACP;
3597 setText.flags = ST_DEFAULT;
3598 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3599 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3600 si.cbSize = sizeof(si);
3601 si.fMask = SIF_ALL;
3602 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3603 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3604 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3605 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3606 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3608 setText.codepage = 1200; /* no constant for unicode */
3609 getText.codepage = 1200; /* no constant for unicode */
3610 getText.cb = MAX_BUF_LEN;
3611 getText.flags = GT_DEFAULT;
3612 getText.lpDefaultChar = NULL;
3613 getText.lpUsedDefChar = NULL;
3615 setText.flags = 0;
3616 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3617 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3618 ok(lstrcmpW(buf, TestItem1) == 0,
3619 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3621 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3622 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3624 setText.codepage = 1200; /* no constant for unicode */
3625 getText.codepage = 1200; /* no constant for unicode */
3626 getText.cb = MAX_BUF_LEN;
3627 getText.flags = GT_DEFAULT;
3628 getText.lpDefaultChar = NULL;
3629 getText.lpUsedDefChar = NULL;
3630 setText.flags = 0;
3631 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3632 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3633 ok(lstrcmpW(buf, TestItem2) == 0,
3634 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3636 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3637 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3638 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3639 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3641 /* Baseline test for just-enough buffer space for string */
3642 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3643 getText.codepage = 1200; /* no constant for unicode */
3644 getText.flags = GT_DEFAULT;
3645 getText.lpDefaultChar = NULL;
3646 getText.lpUsedDefChar = NULL;
3647 memset(buf, 0, MAX_BUF_LEN);
3648 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3649 ok(lstrcmpW(buf, TestItem2) == 0,
3650 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3652 /* When there is enough space for one character, but not both, of the CRLF
3653 pair at the end of the string, the CR is not copied at all. That is,
3654 the caller must not see CRLF pairs truncated to CR at the end of the
3655 string.
3657 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3658 getText.codepage = 1200; /* no constant for unicode */
3659 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3660 getText.lpDefaultChar = NULL;
3661 getText.lpUsedDefChar = NULL;
3662 memset(buf, 0, MAX_BUF_LEN);
3663 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3664 ok(lstrcmpW(buf, TestItem1) == 0,
3665 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3668 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3669 setText.codepage = 1200; /* no constant for unicode */
3670 getText.codepage = 1200; /* no constant for unicode */
3671 getText.cb = MAX_BUF_LEN;
3672 getText.flags = GT_DEFAULT;
3673 getText.lpDefaultChar = NULL;
3674 getText.lpUsedDefChar = NULL;
3675 setText.flags = 0;
3676 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3677 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3678 ok(lstrcmpW(buf, TestItem3_after) == 0,
3679 "EM_SETTEXTEX did not convert properly\n");
3681 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3682 setText.codepage = 1200; /* no constant for unicode */
3683 getText.codepage = 1200; /* no constant for unicode */
3684 getText.cb = MAX_BUF_LEN;
3685 getText.flags = GT_DEFAULT;
3686 getText.lpDefaultChar = NULL;
3687 getText.lpUsedDefChar = NULL;
3688 setText.flags = 0;
3689 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3690 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3691 ok(lstrcmpW(buf, TestItem3_after) == 0,
3692 "EM_SETTEXTEX did not convert properly\n");
3694 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3695 setText.codepage = 1200; /* no constant for unicode */
3696 getText.codepage = 1200; /* no constant for unicode */
3697 getText.cb = MAX_BUF_LEN;
3698 getText.flags = GT_DEFAULT;
3699 getText.lpDefaultChar = NULL;
3700 getText.lpUsedDefChar = NULL;
3701 setText.flags = 0;
3702 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3703 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3704 ok(lstrcmpW(buf, TestItem4_after) == 0,
3705 "EM_SETTEXTEX did not convert properly\n");
3707 /* !ST_SELECTION && Unicode && !\rtf */
3708 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3709 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3711 ok (result == 1,
3712 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3713 ok(lstrlenW(buf) == 0,
3714 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3716 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3717 setText.flags = 0;
3718 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3719 /* select some text */
3720 cr.cpMax = 1;
3721 cr.cpMin = 3;
3722 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3723 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3724 setText.flags = ST_SELECTION;
3725 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3726 ok(result == 0,
3727 "EM_SETTEXTEX with NULL lParam to replace selection"
3728 " with no text should return 0. Got %i\n",
3729 result);
3731 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3732 setText.flags = 0;
3733 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3734 /* select some text */
3735 cr.cpMax = 1;
3736 cr.cpMin = 3;
3737 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3738 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3739 setText.flags = ST_SELECTION;
3740 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3741 (WPARAM)&setText, (LPARAM) TestItem1);
3742 /* get text */
3743 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3744 ok(result == lstrlenW(TestItem1),
3745 "EM_SETTEXTEX with NULL lParam to replace selection"
3746 " with no text should return 0. Got %i\n",
3747 result);
3748 ok(lstrlenW(buf) == 22,
3749 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3750 lstrlenW(buf) );
3752 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3753 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3754 p = (char *)buf;
3755 es.dwCookie = (DWORD_PTR)&p;
3756 es.dwError = 0;
3757 es.pfnCallback = test_WM_SETTEXT_esCallback;
3758 memset(buf, 0, sizeof(buf));
3759 SendMessage(hwndRichEdit, EM_STREAMOUT,
3760 (WPARAM)(SF_RTF), (LPARAM)&es);
3761 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3763 /* !ST_SELECTION && !Unicode && \rtf */
3764 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3765 getText.codepage = 1200; /* no constant for unicode */
3766 getText.cb = MAX_BUF_LEN;
3767 getText.flags = GT_DEFAULT;
3768 getText.lpDefaultChar = NULL;
3769 getText.lpUsedDefChar = NULL;
3771 setText.flags = 0;
3772 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3773 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3774 ok(lstrcmpW(buf, TestItem1) == 0,
3775 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3777 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3778 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3779 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3780 getText.codepage = CP_ACP;
3781 getText.cb = MAX_BUF_LEN;
3782 getText.flags = GT_DEFAULT;
3783 getText.lpDefaultChar = NULL;
3784 getText.lpUsedDefChar = NULL;
3786 setText.flags = ST_SELECTION;
3787 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3788 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3789 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3790 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3791 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3793 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3794 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3795 p = (char *)buf;
3796 es.dwCookie = (DWORD_PTR)&p;
3797 es.dwError = 0;
3798 es.pfnCallback = test_WM_SETTEXT_esCallback;
3799 memset(buf, 0, sizeof(buf));
3800 SendMessage(hwndRichEdit, EM_STREAMOUT,
3801 (WPARAM)(SF_RTF), (LPARAM)&es);
3802 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3804 /* select some text */
3805 cr.cpMax = 1;
3806 cr.cpMin = 3;
3807 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3809 /* ST_SELECTION && !Unicode && \rtf */
3810 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3811 getText.codepage = 1200; /* no constant for unicode */
3812 getText.cb = MAX_BUF_LEN;
3813 getText.flags = GT_DEFAULT;
3814 getText.lpDefaultChar = NULL;
3815 getText.lpUsedDefChar = NULL;
3817 setText.flags = ST_SELECTION;
3818 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3819 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3820 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3822 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3823 setText.codepage = 1200; /* no constant for unicode */
3824 getText.codepage = CP_ACP;
3825 getText.cb = MAX_BUF_LEN;
3827 setText.flags = 0;
3828 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3829 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3831 /* select some text */
3832 cr.cpMax = 1;
3833 cr.cpMin = 3;
3834 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3836 /* ST_SELECTION && !Unicode && !\rtf */
3837 setText.codepage = CP_ACP;
3838 getText.codepage = 1200; /* no constant for unicode */
3839 getText.cb = MAX_BUF_LEN;
3840 getText.flags = GT_DEFAULT;
3841 getText.lpDefaultChar = NULL;
3842 getText.lpUsedDefChar = NULL;
3844 setText.flags = ST_SELECTION;
3845 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3846 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3847 ok(lstrcmpW(buf, TestItem1alt) == 0,
3848 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3849 " using ST_SELECTION and non-Unicode\n");
3851 /* Test setting text using rich text format */
3852 setText.flags = 0;
3853 setText.codepage = CP_ACP;
3854 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3855 getText.codepage = CP_ACP;
3856 getText.cb = MAX_BUF_LEN;
3857 getText.flags = GT_DEFAULT;
3858 getText.lpDefaultChar = NULL;
3859 getText.lpUsedDefChar = NULL;
3860 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3861 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3863 setText.flags = 0;
3864 setText.codepage = CP_ACP;
3865 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3866 getText.codepage = CP_ACP;
3867 getText.cb = MAX_BUF_LEN;
3868 getText.flags = GT_DEFAULT;
3869 getText.lpDefaultChar = NULL;
3870 getText.lpUsedDefChar = NULL;
3871 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3872 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3874 DestroyWindow(hwndRichEdit);
3877 static void test_EM_LIMITTEXT(void)
3879 int ret;
3881 HWND hwndRichEdit = new_richedit(NULL);
3883 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3884 * about setting the length to -1 for multiline edit controls doesn't happen.
3887 /* Don't check default gettextlimit case. That's done in other tests */
3889 /* Set textlimit to 100 */
3890 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3891 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3892 ok (ret == 100,
3893 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3895 /* Set textlimit to 0 */
3896 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3897 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3898 ok (ret == 65536,
3899 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3901 /* Set textlimit to -1 */
3902 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3903 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3904 ok (ret == -1,
3905 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3907 /* Set textlimit to -2 */
3908 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3909 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3910 ok (ret == -2,
3911 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3913 DestroyWindow (hwndRichEdit);
3917 static void test_EM_EXLIMITTEXT(void)
3919 int i, selBegin, selEnd, len1, len2;
3920 int result;
3921 char text[1024 + 1];
3922 char buffer[1024 + 1];
3923 int textlimit = 0; /* multiple of 100 */
3924 HWND hwndRichEdit = new_richedit(NULL);
3926 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3927 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3929 textlimit = 256000;
3930 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3931 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3932 /* set higher */
3933 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3935 textlimit = 1000;
3936 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3937 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3938 /* set lower */
3939 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3941 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3942 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3943 /* default for WParam = 0 */
3944 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3946 textlimit = sizeof(text)-1;
3947 memset(text, 'W', textlimit);
3948 text[sizeof(text)-1] = 0;
3949 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3950 /* maxed out text */
3951 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3953 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3954 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3955 len1 = selEnd - selBegin;
3957 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3958 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3959 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3960 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3961 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3962 len2 = selEnd - selBegin;
3964 ok(len1 != len2,
3965 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3966 len1,len2,i);
3968 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3969 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3970 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3971 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3972 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3973 len1 = selEnd - selBegin;
3975 ok(len1 != len2,
3976 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3977 len1,len2,i);
3979 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3980 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3981 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3982 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3983 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3984 len2 = selEnd - selBegin;
3986 ok(len1 == len2,
3987 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3988 len1,len2,i);
3990 /* set text up to the limit, select all the text, then add a char */
3991 textlimit = 5;
3992 memset(text, 'W', textlimit);
3993 text[textlimit] = 0;
3994 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3995 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3996 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3997 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3998 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3999 result = strcmp(buffer, "A");
4000 ok(0 == result, "got string = \"%s\"\n", buffer);
4002 /* WM_SETTEXT not limited */
4003 textlimit = 10;
4004 memset(text, 'W', textlimit);
4005 text[textlimit] = 0;
4006 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4007 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4008 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4009 i = strlen(buffer);
4010 ok(10 == i, "expected 10 chars\n");
4011 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4012 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4014 /* try inserting more text at end */
4015 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4016 ok(0 == i, "WM_CHAR wasn't processed\n");
4017 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4018 i = strlen(buffer);
4019 ok(10 == i, "expected 10 chars, got %i\n", i);
4020 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4021 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4023 /* try inserting text at beginning */
4024 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4025 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4026 ok(0 == i, "WM_CHAR wasn't processed\n");
4027 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4028 i = strlen(buffer);
4029 ok(10 == i, "expected 10 chars, got %i\n", i);
4030 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4031 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4033 /* WM_CHAR is limited */
4034 textlimit = 1;
4035 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4036 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4037 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4038 ok(0 == i, "WM_CHAR wasn't processed\n");
4039 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4040 ok(0 == i, "WM_CHAR wasn't processed\n");
4041 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4042 i = strlen(buffer);
4043 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4045 DestroyWindow(hwndRichEdit);
4048 static void test_EM_GETLIMITTEXT(void)
4050 int i;
4051 HWND hwndRichEdit = new_richedit(NULL);
4053 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4054 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4056 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4057 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4058 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4060 DestroyWindow(hwndRichEdit);
4063 static void test_WM_SETFONT(void)
4065 /* There is no invalid input or error conditions for this function.
4066 * NULL wParam and lParam just fall back to their default values
4067 * It should be noted that even if you use a gibberish name for your fonts
4068 * here, it will still work because the name is stored. They will display as
4069 * System, but will report their name to be whatever they were created as */
4071 HWND hwndRichEdit = new_richedit(NULL);
4072 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4073 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4074 FF_DONTCARE, "Marlett");
4075 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4076 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4077 FF_DONTCARE, "MS Sans Serif");
4078 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4079 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4080 FF_DONTCARE, "Courier");
4081 LOGFONTA sentLogFont;
4082 CHARFORMAT2A returnedCF2A;
4084 returnedCF2A.cbSize = sizeof(returnedCF2A);
4086 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4087 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4088 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4090 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4091 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4092 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4093 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4095 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4096 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4097 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4098 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4099 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4100 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4102 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4103 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4104 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4105 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4106 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4107 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4109 /* This last test is special since we send in NULL. We clear the variables
4110 * and just compare to "System" instead of the sent in font name. */
4111 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4112 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4113 returnedCF2A.cbSize = sizeof(returnedCF2A);
4115 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4116 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4117 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4118 ok (!strcmp("System",returnedCF2A.szFaceName),
4119 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4121 DestroyWindow(hwndRichEdit);
4125 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4126 LPBYTE pbBuff,
4127 LONG cb,
4128 LONG *pcb)
4130 const char** str = (const char**)dwCookie;
4131 int size = strlen(*str);
4132 if(size > 3) /* let's make it piecemeal for fun */
4133 size = 3;
4134 *pcb = cb;
4135 if (*pcb > size) {
4136 *pcb = size;
4138 if (*pcb > 0) {
4139 memcpy(pbBuff, *str, *pcb);
4140 *str += *pcb;
4142 return 0;
4145 static void test_EM_GETMODIFY(void)
4147 HWND hwndRichEdit = new_richedit(NULL);
4148 LRESULT result;
4149 SETTEXTEX setText;
4150 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4151 'S', 'o', 'm', 'e',
4152 'T', 'e', 'x', 't', 0};
4153 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4154 'S', 'o', 'm', 'e',
4155 'O', 't', 'h', 'e', 'r',
4156 'T', 'e', 'x', 't', 0};
4157 const char* streamText = "hello world";
4158 CHARFORMAT2 cf2;
4159 PARAFORMAT2 pf2;
4160 EDITSTREAM es;
4162 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4163 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4164 FF_DONTCARE, "Courier");
4166 setText.codepage = 1200; /* no constant for unicode */
4167 setText.flags = ST_KEEPUNDO;
4170 /* modify flag shouldn't be set when richedit is first created */
4171 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4172 ok (result == 0,
4173 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4175 /* setting modify flag should actually set it */
4176 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4177 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4178 ok (result != 0,
4179 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4181 /* clearing modify flag should actually clear it */
4182 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4183 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4184 ok (result == 0,
4185 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4187 /* setting font doesn't change modify flag */
4188 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4189 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4190 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4191 ok (result == 0,
4192 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4194 /* setting text should set modify flag */
4195 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4196 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4197 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4198 ok (result != 0,
4199 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4201 /* undo previous text doesn't reset modify flag */
4202 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4203 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4204 ok (result != 0,
4205 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4207 /* set text with no flag to keep undo stack should not set modify flag */
4208 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4209 setText.flags = 0;
4210 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4211 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4212 ok (result == 0,
4213 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4215 /* WM_SETTEXT doesn't modify */
4216 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4217 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4218 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4219 ok (result == 0,
4220 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4222 /* clear the text */
4223 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4224 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4225 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4226 ok (result == 0,
4227 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4229 /* replace text */
4230 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4231 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4232 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4233 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4234 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4235 ok (result != 0,
4236 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4238 /* copy/paste text 1 */
4239 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4240 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4241 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4242 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4243 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4244 ok (result != 0,
4245 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4247 /* copy/paste text 2 */
4248 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4249 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4250 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4251 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4252 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4253 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4254 ok (result != 0,
4255 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4257 /* press char */
4258 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4259 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4260 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4261 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4262 ok (result != 0,
4263 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4265 /* press del */
4266 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4267 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4268 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4269 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4270 ok (result != 0,
4271 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4273 /* set char format */
4274 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4275 cf2.cbSize = sizeof(CHARFORMAT2);
4276 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4277 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4278 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4279 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4280 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4281 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4282 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4283 ok (result != 0,
4284 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4286 /* set para format */
4287 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4288 pf2.cbSize = sizeof(PARAFORMAT2);
4289 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4290 (LPARAM) &pf2);
4291 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4292 pf2.wAlignment = PFA_RIGHT;
4293 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4294 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4295 ok (result == 0,
4296 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4298 /* EM_STREAM */
4299 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4300 es.dwCookie = (DWORD_PTR)&streamText;
4301 es.dwError = 0;
4302 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4303 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4304 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4305 ok (result != 0,
4306 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4308 DestroyWindow(hwndRichEdit);
4311 struct exsetsel_s {
4312 LONG min;
4313 LONG max;
4314 LRESULT expected_retval;
4315 int expected_getsel_start;
4316 int expected_getsel_end;
4317 int _getsel_todo_wine;
4320 const struct exsetsel_s exsetsel_tests[] = {
4321 /* sanity tests */
4322 {5, 10, 10, 5, 10, 0},
4323 {15, 17, 17, 15, 17, 0},
4324 /* test cpMax > strlen() */
4325 {0, 100, 18, 0, 18, 1},
4326 /* test cpMin == cpMax */
4327 {5, 5, 5, 5, 5, 0},
4328 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4329 {-1, 0, 5, 5, 5, 0},
4330 {-1, 17, 5, 5, 5, 0},
4331 {-1, 18, 5, 5, 5, 0},
4332 /* test cpMin < 0 && cpMax < 0 */
4333 {-1, -1, 17, 17, 17, 0},
4334 {-4, -5, 17, 17, 17, 0},
4335 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4336 {0, -1, 18, 0, 18, 1},
4337 {17, -5, 18, 17, 18, 1},
4338 {18, -3, 17, 17, 17, 0},
4339 /* test if cpMin > cpMax */
4340 {15, 19, 18, 15, 18, 1},
4341 {19, 15, 18, 15, 18, 1}
4344 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4345 CHARRANGE cr;
4346 LRESULT result;
4347 int start, end;
4349 cr.cpMin = setsel->min;
4350 cr.cpMax = setsel->max;
4351 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4353 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4355 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4357 if (setsel->_getsel_todo_wine) {
4358 todo_wine {
4359 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);
4361 } else {
4362 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);
4366 static void test_EM_EXSETSEL(void)
4368 HWND hwndRichEdit = new_richedit(NULL);
4369 int i;
4370 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4372 /* sending some text to the window */
4373 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4374 /* 01234567890123456*/
4375 /* 10 */
4377 for (i = 0; i < num_tests; i++) {
4378 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4381 DestroyWindow(hwndRichEdit);
4384 static void test_EM_REPLACESEL(int redraw)
4386 HWND hwndRichEdit = new_richedit(NULL);
4387 char buffer[1024] = {0};
4388 int r;
4389 GETTEXTEX getText;
4390 CHARRANGE cr;
4392 /* sending some text to the window */
4393 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4394 /* 01234567890123456*/
4395 /* 10 */
4397 /* FIXME add more tests */
4398 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4399 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4400 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4401 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4402 r = strcmp(buffer, "testing");
4403 ok(0 == r, "expected %d, got %d\n", 0, r);
4405 DestroyWindow(hwndRichEdit);
4407 hwndRichEdit = new_richedit(NULL);
4409 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4410 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4412 /* Test behavior with carriage returns and newlines */
4413 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4414 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4415 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4416 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4417 r = strcmp(buffer, "RichEdit1");
4418 ok(0 == r, "expected %d, got %d\n", 0, r);
4419 getText.cb = 1024;
4420 getText.codepage = CP_ACP;
4421 getText.flags = GT_DEFAULT;
4422 getText.lpDefaultChar = NULL;
4423 getText.lpUsedDefChar = NULL;
4424 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4425 ok(strcmp(buffer, "RichEdit1") == 0,
4426 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4428 /* Test number of lines reported after EM_REPLACESEL */
4429 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4430 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4432 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4433 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4434 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4435 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4436 r = strcmp(buffer, "RichEdit1\r\n");
4437 ok(0 == r, "expected %d, got %d\n", 0, r);
4438 getText.cb = 1024;
4439 getText.codepage = CP_ACP;
4440 getText.flags = GT_DEFAULT;
4441 getText.lpDefaultChar = NULL;
4442 getText.lpUsedDefChar = NULL;
4443 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4444 ok(strcmp(buffer, "RichEdit1\r") == 0,
4445 "EM_GETTEXTEX returned incorrect string\n");
4447 /* Test number of lines reported after EM_REPLACESEL */
4448 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4449 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4451 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4452 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4453 returns the number of characters *inserted* into the control (after
4454 required conversions), but WinXP's riched20 returns the number of
4455 characters interpreted from the original lParam. Wine's builtin riched20
4456 implements the WinXP behavior.
4458 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4459 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4460 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4461 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4463 /* Test number of lines reported after EM_REPLACESEL */
4464 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4465 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4467 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4468 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4469 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4470 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4472 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4473 r = strcmp(buffer, "RichEdit1\r\n");
4474 ok(0 == r, "expected %d, got %d\n", 0, r);
4475 getText.cb = 1024;
4476 getText.codepage = CP_ACP;
4477 getText.flags = GT_DEFAULT;
4478 getText.lpDefaultChar = NULL;
4479 getText.lpUsedDefChar = NULL;
4480 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4481 ok(strcmp(buffer, "RichEdit1\r") == 0,
4482 "EM_GETTEXTEX returned incorrect string\n");
4484 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4485 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4486 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4487 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4489 /* The following tests show that richedit should handle the special \r\r\n
4490 sequence by turning it into a single space on insertion. However,
4491 EM_REPLACESEL on WinXP returns the number of characters in the original
4492 string.
4495 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4496 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4497 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4498 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4499 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4500 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4501 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4503 /* Test the actual string */
4504 getText.cb = 1024;
4505 getText.codepage = CP_ACP;
4506 getText.flags = GT_DEFAULT;
4507 getText.lpDefaultChar = NULL;
4508 getText.lpUsedDefChar = NULL;
4509 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4510 ok(strcmp(buffer, "\r\r") == 0,
4511 "EM_GETTEXTEX returned incorrect string\n");
4513 /* Test number of lines reported after EM_REPLACESEL */
4514 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4515 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4517 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4518 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4519 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4520 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4521 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4522 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4523 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4524 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4526 /* Test the actual string */
4527 getText.cb = 1024;
4528 getText.codepage = CP_ACP;
4529 getText.flags = GT_DEFAULT;
4530 getText.lpDefaultChar = NULL;
4531 getText.lpUsedDefChar = NULL;
4532 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4533 ok(strcmp(buffer, " ") == 0,
4534 "EM_GETTEXTEX returned incorrect string\n");
4536 /* Test number of lines reported after EM_REPLACESEL */
4537 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4538 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4541 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4542 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4543 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4544 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4545 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4546 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4547 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4549 /* Test the actual string */
4550 getText.cb = 1024;
4551 getText.codepage = CP_ACP;
4552 getText.flags = GT_DEFAULT;
4553 getText.lpDefaultChar = NULL;
4554 getText.lpUsedDefChar = NULL;
4555 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4556 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4557 "EM_GETTEXTEX returned incorrect string\n");
4559 /* Test number of lines reported after EM_REPLACESEL */
4560 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4561 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4563 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4564 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4565 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4566 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4567 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4568 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4569 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4570 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4572 /* Test the actual string */
4573 getText.cb = 1024;
4574 getText.codepage = CP_ACP;
4575 getText.flags = GT_DEFAULT;
4576 getText.lpDefaultChar = NULL;
4577 getText.lpUsedDefChar = NULL;
4578 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4579 ok(strcmp(buffer, " \r") == 0,
4580 "EM_GETTEXTEX returned incorrect string\n");
4582 /* Test number of lines reported after EM_REPLACESEL */
4583 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4584 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4586 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4587 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4588 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4589 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4590 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4591 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4592 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4593 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4595 /* Test the actual string */
4596 getText.cb = 1024;
4597 getText.codepage = CP_ACP;
4598 getText.flags = GT_DEFAULT;
4599 getText.lpDefaultChar = NULL;
4600 getText.lpUsedDefChar = NULL;
4601 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4602 ok(strcmp(buffer, " \r\r") == 0,
4603 "EM_GETTEXTEX returned incorrect string\n");
4605 /* Test number of lines reported after EM_REPLACESEL */
4606 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4607 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4609 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4610 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4611 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4612 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4613 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4614 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4615 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4616 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4618 /* Test the actual string */
4619 getText.cb = 1024;
4620 getText.codepage = CP_ACP;
4621 getText.flags = GT_DEFAULT;
4622 getText.lpDefaultChar = NULL;
4623 getText.lpUsedDefChar = NULL;
4624 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4625 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4626 "EM_GETTEXTEX returned incorrect string\n");
4628 /* Test number of lines reported after EM_REPLACESEL */
4629 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4630 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4632 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4633 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4634 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4635 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4636 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4637 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4638 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4640 /* Test the actual string */
4641 getText.cb = 1024;
4642 getText.codepage = CP_ACP;
4643 getText.flags = GT_DEFAULT;
4644 getText.lpDefaultChar = NULL;
4645 getText.lpUsedDefChar = NULL;
4646 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4647 ok(strcmp(buffer, "\r\r") == 0,
4648 "EM_GETTEXTEX returned incorrect string\n");
4650 /* Test number of lines reported after EM_REPLACESEL */
4651 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4652 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4654 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4655 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4656 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4657 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4658 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4659 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4660 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4661 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4663 /* Test the actual string */
4664 getText.cb = 1024;
4665 getText.codepage = CP_ACP;
4666 getText.flags = GT_DEFAULT;
4667 getText.lpDefaultChar = NULL;
4668 getText.lpUsedDefChar = NULL;
4669 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4670 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4671 "EM_GETTEXTEX returned incorrect string\n");
4673 /* Test number of lines reported after EM_REPLACESEL */
4674 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4675 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4677 if (!redraw)
4678 /* This is needed to avoid interferring with keybd_event calls
4679 * on other tests that simulate keyboard events. */
4680 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4682 DestroyWindow(hwndRichEdit);
4685 static void test_WM_PASTE(void)
4687 int result;
4688 char buffer[1024] = {0};
4689 const char* text1 = "testing paste\r";
4690 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4691 const char* text1_after = "testing paste\r\n";
4692 const char* text2 = "testing paste\r\rtesting paste";
4693 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4694 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4695 HWND hwndRichEdit = new_richedit(NULL);
4697 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4698 * to test the state of the modifiers (Ctrl/Alt/Shift).
4700 * Therefore Ctrl-<key> keystrokes need to be simulated with
4701 * keybd_event or by using SetKeyboardState to set the modifiers
4702 * and SendMessage to simulate the keystrokes.
4705 /* Sent keystrokes with keybd_event */
4706 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4707 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4708 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4709 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4710 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4712 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4713 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4715 SEND_CTRL_C(hwndRichEdit); /* Copy */
4716 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4717 SEND_CTRL_V(hwndRichEdit); /* Paste */
4718 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4719 /* Pasted text should be visible at this step */
4720 result = strcmp(text1_step1, buffer);
4721 ok(result == 0,
4722 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4724 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4725 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4726 /* Text should be the same as before (except for \r -> \r\n conversion) */
4727 result = strcmp(text1_after, buffer);
4728 ok(result == 0,
4729 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4731 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4732 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4733 SEND_CTRL_C(hwndRichEdit); /* Copy */
4734 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4735 SEND_CTRL_V(hwndRichEdit); /* Paste */
4736 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4737 /* Pasted text should be visible at this step */
4738 result = strcmp(text3, buffer);
4739 ok(result == 0,
4740 "test paste: strcmp = %i\n", result);
4741 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4742 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4743 /* Text should be the same as before (except for \r -> \r\n conversion) */
4744 result = strcmp(text2_after, buffer);
4745 ok(result == 0,
4746 "test paste: strcmp = %i\n", result);
4747 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4748 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4749 /* Text should revert to post-paste state */
4750 result = strcmp(buffer,text3);
4751 ok(result == 0,
4752 "test paste: strcmp = %i\n", result);
4754 #undef SEND_CTRL_C
4755 #undef SEND_CTRL_X
4756 #undef SEND_CTRL_V
4757 #undef SEND_CTRL_Z
4758 #undef SEND_CTRL_Y
4760 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4761 /* Send WM_CHAR to simulates Ctrl-V */
4762 SendMessage(hwndRichEdit, WM_CHAR, 22,
4763 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4764 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4765 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4766 result = strcmp(buffer,"");
4767 ok(result == 0,
4768 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4770 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4771 * with SetKeyboard state. */
4773 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4774 /* Simulates paste (Ctrl-V) */
4775 hold_key(VK_CONTROL);
4776 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4777 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4778 release_key(VK_CONTROL);
4779 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4780 result = strcmp(buffer,"paste");
4781 ok(result == 0,
4782 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4784 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4785 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4786 /* Simulates copy (Ctrl-C) */
4787 hold_key(VK_CONTROL);
4788 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4789 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4790 release_key(VK_CONTROL);
4791 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4792 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4793 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4794 result = strcmp(buffer,"testing");
4795 ok(result == 0,
4796 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4798 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4799 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4800 /* Simulates select all (Ctrl-A) */
4801 hold_key(VK_CONTROL);
4802 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4803 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4804 /* Simulates select cut (Ctrl-X) */
4805 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4806 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4807 release_key(VK_CONTROL);
4808 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4809 result = strcmp(buffer,"");
4810 ok(result == 0,
4811 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4812 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4813 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4814 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4815 result = strcmp(buffer,"cut\r\n");
4816 todo_wine ok(result == 0,
4817 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4818 /* Simulates undo (Ctrl-Z) */
4819 hold_key(VK_CONTROL);
4820 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4821 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4822 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4823 result = strcmp(buffer,"");
4824 ok(result == 0,
4825 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4826 /* Simulates redo (Ctrl-Y) */
4827 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4828 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4829 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4830 result = strcmp(buffer,"cut\r\n");
4831 todo_wine ok(result == 0,
4832 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4833 release_key(VK_CONTROL);
4835 DestroyWindow(hwndRichEdit);
4838 static void test_EM_FORMATRANGE(void)
4840 int r, i, tpp_x, tpp_y;
4841 HDC hdc;
4842 HWND hwndRichEdit = new_richedit(NULL);
4843 FORMATRANGE fr;
4844 BOOL skip_non_english;
4845 static const struct {
4846 const char *string; /* The string */
4847 int first; /* First 'pagebreak', 0 for don't care */
4848 int second; /* Second 'pagebreak', 0 for don't care */
4849 } fmtstrings[] = {
4850 {"WINE wine", 0, 0},
4851 {"WINE wineWine", 0, 0},
4852 {"WINE\r\nwine\r\nwine", 5, 10},
4853 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4854 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4857 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4858 if (skip_non_english)
4859 skip("Skipping some tests on non-English platform\n");
4861 hdc = GetDC(hwndRichEdit);
4862 ok(hdc != NULL, "Could not get HDC\n");
4864 /* Calculate the twips per pixel */
4865 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4866 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4868 /* Test the simple case where all the text fits in the page rect. */
4869 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4870 fr.hdc = fr.hdcTarget = hdc;
4871 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4872 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4873 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4874 fr.chrg.cpMin = 0;
4875 fr.chrg.cpMax = -1;
4876 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4877 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4879 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4880 fr.rc.bottom = fr.rcPage.bottom;
4881 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4882 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4884 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4886 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4888 GETTEXTLENGTHEX gtl;
4889 SIZE stringsize;
4890 int len;
4892 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4894 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4895 gtl.codepage = CP_ACP;
4896 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4898 /* Get some size information for the string */
4899 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4901 /* Define the box to be half the width needed and a bit larger than the height.
4902 * Changes to the width means we have at least 2 pages. Changes to the height
4903 * is done so we can check the changing of fr.rc.bottom.
4905 fr.hdc = fr.hdcTarget = hdc;
4906 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4907 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4908 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4910 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4911 todo_wine {
4912 ok(r == len, "Expected %d, got %d\n", len, r);
4915 /* We know that the page can't hold the full string. See how many characters
4916 * are on the first one
4918 fr.chrg.cpMin = 0;
4919 fr.chrg.cpMax = -1;
4920 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4921 todo_wine {
4922 if (! skip_non_english)
4923 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4925 if (fmtstrings[i].first)
4926 todo_wine {
4927 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4929 else
4930 ok(r < len, "Expected < %d, got %d\n", len, r);
4932 /* Do another page */
4933 fr.chrg.cpMin = r;
4934 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4935 if (fmtstrings[i].second)
4936 todo_wine {
4937 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4939 else if (! skip_non_english)
4940 ok (r < len, "Expected < %d, got %d\n", len, r);
4942 /* There is at least on more page, but we don't care */
4944 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4945 todo_wine {
4946 ok(r == len, "Expected %d, got %d\n", len, r);
4950 ReleaseDC(NULL, hdc);
4951 DestroyWindow(hwndRichEdit);
4954 static int nCallbackCount = 0;
4956 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4957 LONG cb, LONG* pcb)
4959 const char text[] = {'t','e','s','t'};
4961 if (sizeof(text) <= cb)
4963 if ((int)dwCookie != nCallbackCount)
4965 *pcb = 0;
4966 return 0;
4969 memcpy (pbBuff, text, sizeof(text));
4970 *pcb = sizeof(text);
4972 nCallbackCount++;
4974 return 0;
4976 else
4977 return 1; /* indicates callback failed */
4980 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4981 LPBYTE pbBuff,
4982 LONG cb,
4983 LONG *pcb)
4985 const char** str = (const char**)dwCookie;
4986 int size = strlen(*str);
4987 *pcb = cb;
4988 if (*pcb > size) {
4989 *pcb = size;
4991 if (*pcb > 0) {
4992 memcpy(pbBuff, *str, *pcb);
4993 *str += *pcb;
4995 return 0;
4998 struct StringWithLength {
4999 int length;
5000 char *buffer;
5003 /* This callback is used to handled the null characters in a string. */
5004 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5005 LPBYTE pbBuff,
5006 LONG cb,
5007 LONG *pcb)
5009 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5010 int size = str->length;
5011 *pcb = cb;
5012 if (*pcb > size) {
5013 *pcb = size;
5015 if (*pcb > 0) {
5016 memcpy(pbBuff, str->buffer, *pcb);
5017 str->buffer += *pcb;
5018 str->length -= *pcb;
5020 return 0;
5023 static void test_EM_STREAMIN(void)
5025 HWND hwndRichEdit = new_richedit(NULL);
5026 LRESULT result;
5027 EDITSTREAM es;
5028 char buffer[1024] = {0};
5030 const char * streamText0 = "{\\rtf1 TestSomeText}";
5031 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5032 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5034 const char * streamText1 =
5035 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5036 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5037 "}\r\n";
5039 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5040 const char * streamText2 =
5041 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5042 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5043 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5044 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5045 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5046 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5047 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5049 const char * streamText3 = "RichEdit1";
5051 struct StringWithLength cookieForStream4;
5052 const char * streamText4 =
5053 "This text just needs to be long enough to cause run to be split onto "
5054 "two separate lines and make sure the null terminating character is "
5055 "handled properly.\0";
5056 int length4 = strlen(streamText4) + 1;
5057 cookieForStream4.buffer = (char *)streamText4;
5058 cookieForStream4.length = length4;
5060 /* Minimal test without \par at the end */
5061 es.dwCookie = (DWORD_PTR)&streamText0;
5062 es.dwError = 0;
5063 es.pfnCallback = test_EM_STREAMIN_esCallback;
5064 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5066 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5067 ok (result == 12,
5068 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5069 result = strcmp (buffer,"TestSomeText");
5070 ok (result == 0,
5071 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5072 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5074 /* Native richedit 2.0 ignores last \par */
5075 es.dwCookie = (DWORD_PTR)&streamText0a;
5076 es.dwError = 0;
5077 es.pfnCallback = test_EM_STREAMIN_esCallback;
5078 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5080 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5081 ok (result == 12,
5082 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5083 result = strcmp (buffer,"TestSomeText");
5084 ok (result == 0,
5085 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5086 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5088 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5089 es.dwCookie = (DWORD_PTR)&streamText0b;
5090 es.dwError = 0;
5091 es.pfnCallback = test_EM_STREAMIN_esCallback;
5092 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5094 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5095 ok (result == 14,
5096 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5097 result = strcmp (buffer,"TestSomeText\r\n");
5098 ok (result == 0,
5099 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5100 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5102 es.dwCookie = (DWORD_PTR)&streamText1;
5103 es.dwError = 0;
5104 es.pfnCallback = test_EM_STREAMIN_esCallback;
5105 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5107 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5108 ok (result == 12,
5109 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5110 result = strcmp (buffer,"TestSomeText");
5111 ok (result == 0,
5112 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5113 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5115 es.dwCookie = (DWORD_PTR)&streamText2;
5116 es.dwError = 0;
5117 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5119 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5120 ok (result == 0,
5121 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5122 ok (strlen(buffer) == 0,
5123 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5124 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5126 es.dwCookie = (DWORD_PTR)&streamText3;
5127 es.dwError = 0;
5128 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5130 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5131 ok (result == 0,
5132 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5133 ok (strlen(buffer) == 0,
5134 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5135 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5137 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5138 es.dwError = 0;
5139 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5140 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5142 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5143 ok (result == length4,
5144 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5145 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5147 DestroyWindow(hwndRichEdit);
5150 static void test_EM_StreamIn_Undo(void)
5152 /* The purpose of this test is to determine when a EM_StreamIn should be
5153 * undoable. This is important because WM_PASTE currently uses StreamIn and
5154 * pasting should always be undoable but streaming isn't always.
5156 * cases to test:
5157 * StreamIn plain text without SFF_SELECTION.
5158 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5159 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5160 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5161 * Feel free to add tests for other text modes or StreamIn things.
5165 HWND hwndRichEdit = new_richedit(NULL);
5166 LRESULT result;
5167 EDITSTREAM es;
5168 char buffer[1024] = {0};
5169 const char randomtext[] = "Some text";
5171 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5173 /* StreamIn, no SFF_SELECTION */
5174 es.dwCookie = nCallbackCount;
5175 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5176 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5177 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5178 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5179 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5180 result = strcmp (buffer,"test");
5181 ok (result == 0,
5182 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5184 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5185 ok (result == FALSE,
5186 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5188 /* StreamIn, SFF_SELECTION, but nothing selected */
5189 es.dwCookie = nCallbackCount;
5190 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5191 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5192 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5193 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5194 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5195 result = strcmp (buffer,"testSome text");
5196 ok (result == 0,
5197 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5199 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5200 ok (result == TRUE,
5201 "EM_STREAMIN with SFF_SELECTION but no selection set "
5202 "should create an undo\n");
5204 /* StreamIn, SFF_SELECTION, with a selection */
5205 es.dwCookie = nCallbackCount;
5206 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5207 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5208 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5209 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5210 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5211 result = strcmp (buffer,"Sometesttext");
5212 ok (result == 0,
5213 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5215 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5216 ok (result == TRUE,
5217 "EM_STREAMIN with SFF_SELECTION and selection set "
5218 "should create an undo\n");
5220 DestroyWindow(hwndRichEdit);
5223 static BOOL is_em_settextex_supported(HWND hwnd)
5225 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5226 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5229 static void test_unicode_conversions(void)
5231 static const WCHAR tW[] = {'t',0};
5232 static const WCHAR teW[] = {'t','e',0};
5233 static const WCHAR textW[] = {'t','e','s','t',0};
5234 static const char textA[] = "test";
5235 char bufA[64];
5236 WCHAR bufW[64];
5237 HWND hwnd;
5238 int em_settextex_supported, ret;
5240 #define set_textA(hwnd, wm_set_text, txt) \
5241 do { \
5242 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5243 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5244 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5245 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5246 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5247 } while(0)
5248 #define expect_textA(hwnd, wm_get_text, txt) \
5249 do { \
5250 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5251 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5252 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5253 memset(bufA, 0xAA, sizeof(bufA)); \
5254 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5255 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5256 ret = lstrcmpA(bufA, txt); \
5257 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5258 } while(0)
5260 #define set_textW(hwnd, wm_set_text, txt) \
5261 do { \
5262 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5263 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5264 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5265 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5266 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5267 } while(0)
5268 #define expect_textW(hwnd, wm_get_text, txt) \
5269 do { \
5270 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5271 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5272 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5273 memset(bufW, 0xAA, sizeof(bufW)); \
5274 if (is_win9x) \
5276 assert(wm_get_text == EM_GETTEXTEX); \
5277 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5278 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5280 else \
5282 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5283 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5285 ret = lstrcmpW(bufW, txt); \
5286 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5287 } while(0)
5288 #define expect_empty(hwnd, wm_get_text) \
5289 do { \
5290 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5291 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5292 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5293 memset(bufA, 0xAA, sizeof(bufA)); \
5294 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5295 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5296 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5297 } while(0)
5299 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5300 0, 0, 200, 60, 0, 0, 0, 0);
5301 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5303 ret = IsWindowUnicode(hwnd);
5304 if (is_win9x)
5305 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
5306 else
5307 ok(ret, "RichEdit20W should be unicode under NT\n");
5309 /* EM_SETTEXTEX is supported starting from version 3.0 */
5310 em_settextex_supported = is_em_settextex_supported(hwnd);
5311 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5312 em_settextex_supported ? "" : "NOT ");
5314 expect_empty(hwnd, WM_GETTEXT);
5315 expect_empty(hwnd, EM_GETTEXTEX);
5317 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5318 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5319 expect_textA(hwnd, WM_GETTEXT, "t");
5320 expect_textA(hwnd, EM_GETTEXTEX, "t");
5321 expect_textW(hwnd, EM_GETTEXTEX, tW);
5323 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5324 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5325 expect_textA(hwnd, WM_GETTEXT, "te");
5326 expect_textA(hwnd, EM_GETTEXTEX, "te");
5327 expect_textW(hwnd, EM_GETTEXTEX, teW);
5329 set_textA(hwnd, WM_SETTEXT, NULL);
5330 expect_empty(hwnd, WM_GETTEXT);
5331 expect_empty(hwnd, EM_GETTEXTEX);
5333 if (is_win9x)
5334 set_textA(hwnd, WM_SETTEXT, textW);
5335 else
5336 set_textA(hwnd, WM_SETTEXT, textA);
5337 expect_textA(hwnd, WM_GETTEXT, textA);
5338 expect_textA(hwnd, EM_GETTEXTEX, textA);
5339 expect_textW(hwnd, EM_GETTEXTEX, textW);
5341 if (em_settextex_supported)
5343 set_textA(hwnd, EM_SETTEXTEX, textA);
5344 expect_textA(hwnd, WM_GETTEXT, textA);
5345 expect_textA(hwnd, EM_GETTEXTEX, textA);
5346 expect_textW(hwnd, EM_GETTEXTEX, textW);
5349 if (!is_win9x)
5351 set_textW(hwnd, WM_SETTEXT, textW);
5352 expect_textW(hwnd, WM_GETTEXT, textW);
5353 expect_textA(hwnd, WM_GETTEXT, textA);
5354 expect_textW(hwnd, EM_GETTEXTEX, textW);
5355 expect_textA(hwnd, EM_GETTEXTEX, textA);
5357 if (em_settextex_supported)
5359 set_textW(hwnd, EM_SETTEXTEX, textW);
5360 expect_textW(hwnd, WM_GETTEXT, textW);
5361 expect_textA(hwnd, WM_GETTEXT, textA);
5362 expect_textW(hwnd, EM_GETTEXTEX, textW);
5363 expect_textA(hwnd, EM_GETTEXTEX, textA);
5366 DestroyWindow(hwnd);
5368 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5369 0, 0, 200, 60, 0, 0, 0, 0);
5370 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5372 ret = IsWindowUnicode(hwnd);
5373 ok(!ret, "RichEdit20A should NOT be unicode\n");
5375 set_textA(hwnd, WM_SETTEXT, textA);
5376 expect_textA(hwnd, WM_GETTEXT, textA);
5377 expect_textA(hwnd, EM_GETTEXTEX, textA);
5378 expect_textW(hwnd, EM_GETTEXTEX, textW);
5380 if (em_settextex_supported)
5382 set_textA(hwnd, EM_SETTEXTEX, textA);
5383 expect_textA(hwnd, WM_GETTEXT, textA);
5384 expect_textA(hwnd, EM_GETTEXTEX, textA);
5385 expect_textW(hwnd, EM_GETTEXTEX, textW);
5388 if (!is_win9x)
5390 set_textW(hwnd, WM_SETTEXT, textW);
5391 expect_textW(hwnd, WM_GETTEXT, textW);
5392 expect_textA(hwnd, WM_GETTEXT, textA);
5393 expect_textW(hwnd, EM_GETTEXTEX, textW);
5394 expect_textA(hwnd, EM_GETTEXTEX, textA);
5396 if (em_settextex_supported)
5398 set_textW(hwnd, EM_SETTEXTEX, textW);
5399 expect_textW(hwnd, WM_GETTEXT, textW);
5400 expect_textA(hwnd, WM_GETTEXT, textA);
5401 expect_textW(hwnd, EM_GETTEXTEX, textW);
5402 expect_textA(hwnd, EM_GETTEXTEX, textA);
5405 DestroyWindow(hwnd);
5408 static void test_WM_CHAR(void)
5410 HWND hwnd;
5411 int ret;
5412 const char * char_list = "abc\rabc\r";
5413 const char * expected_content_single = "abcabc";
5414 const char * expected_content_multi = "abc\r\nabc\r\n";
5415 char buffer[64] = {0};
5416 const char * p;
5418 /* single-line control must IGNORE carriage returns */
5419 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5420 0, 0, 200, 60, 0, 0, 0, 0);
5421 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5423 p = char_list;
5424 while (*p != '\0') {
5425 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5426 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5427 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5428 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5429 p++;
5432 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5433 ret = strcmp(buffer, expected_content_single);
5434 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5436 DestroyWindow(hwnd);
5438 /* multi-line control inserts CR normally */
5439 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5440 0, 0, 200, 60, 0, 0, 0, 0);
5441 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5443 p = char_list;
5444 while (*p != '\0') {
5445 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5446 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5447 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5448 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5449 p++;
5452 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5453 ret = strcmp(buffer, expected_content_multi);
5454 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5456 DestroyWindow(hwnd);
5459 static void test_EM_GETTEXTLENGTHEX(void)
5461 HWND hwnd;
5462 GETTEXTLENGTHEX gtl;
5463 int ret;
5464 const char * base_string = "base string";
5465 const char * test_string = "a\nb\n\n\r\n";
5466 const char * test_string_after = "a";
5467 const char * test_string_2 = "a\rtest\rstring";
5468 char buffer[64] = {0};
5470 /* single line */
5471 if (!is_win9x)
5472 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5473 0, 0, 200, 60, 0, 0, 0, 0);
5474 else
5475 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5476 0, 0, 200, 60, 0, 0, 0, 0);
5477 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5479 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5480 gtl.codepage = CP_ACP;
5481 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5482 ok(ret == 0, "ret %d\n",ret);
5484 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5485 gtl.codepage = CP_ACP;
5486 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5487 ok(ret == 0, "ret %d\n",ret);
5489 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5491 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5492 gtl.codepage = CP_ACP;
5493 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5494 ok(ret == strlen(base_string), "ret %d\n",ret);
5496 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5497 gtl.codepage = CP_ACP;
5498 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5499 ok(ret == strlen(base_string), "ret %d\n",ret);
5501 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5503 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5504 gtl.codepage = CP_ACP;
5505 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5506 ok(ret == 1, "ret %d\n",ret);
5508 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5509 gtl.codepage = CP_ACP;
5510 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5511 ok(ret == 1, "ret %d\n",ret);
5513 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5514 ret = strcmp(buffer, test_string_after);
5515 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5517 DestroyWindow(hwnd);
5519 /* multi line */
5520 if (!is_win9x)
5521 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5522 0, 0, 200, 60, 0, 0, 0, 0);
5523 else
5524 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE,
5525 0, 0, 200, 60, 0, 0, 0, 0);
5526 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5528 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5529 gtl.codepage = CP_ACP;
5530 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5531 ok(ret == 0, "ret %d\n",ret);
5533 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5534 gtl.codepage = CP_ACP;
5535 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5536 ok(ret == 0, "ret %d\n",ret);
5538 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5540 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5541 gtl.codepage = CP_ACP;
5542 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5543 ok(ret == strlen(base_string), "ret %d\n",ret);
5545 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5546 gtl.codepage = CP_ACP;
5547 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5548 ok(ret == strlen(base_string), "ret %d\n",ret);
5550 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5552 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5553 gtl.codepage = CP_ACP;
5554 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5555 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5557 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5558 gtl.codepage = CP_ACP;
5559 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5560 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5562 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5564 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5565 gtl.codepage = CP_ACP;
5566 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5567 ok(ret == 10, "ret %d\n",ret);
5569 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5570 gtl.codepage = CP_ACP;
5571 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5572 ok(ret == 6, "ret %d\n",ret);
5574 /* Unicode/NUMCHARS/NUMBYTES */
5575 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5577 gtl.flags = GTL_DEFAULT;
5578 gtl.codepage = 1200;
5579 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5580 ok(ret == lstrlen(test_string_2),
5581 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlen(test_string_2));
5583 gtl.flags = GTL_NUMCHARS;
5584 gtl.codepage = 1200;
5585 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5586 ok(ret == lstrlen(test_string_2),
5587 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlen(test_string_2));
5589 gtl.flags = GTL_NUMBYTES;
5590 gtl.codepage = 1200;
5591 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5592 ok(ret == lstrlen(test_string_2)*2,
5593 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5595 gtl.flags = GTL_PRECISE;
5596 gtl.codepage = 1200;
5597 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5598 ok(ret == lstrlen(test_string_2)*2,
5599 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5601 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5602 gtl.codepage = 1200;
5603 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5604 ok(ret == lstrlen(test_string_2),
5605 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2));
5607 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5608 gtl.codepage = 1200;
5609 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5610 ok(ret == E_INVALIDARG,
5611 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5613 DestroyWindow(hwnd);
5617 /* globals that parent and child access when checking event masks & notifications */
5618 static HWND eventMaskEditHwnd = 0;
5619 static int queriedEventMask;
5620 static int watchForEventMask = 0;
5622 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5623 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5625 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5627 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5629 return DefWindowProcA(hwnd, message, wParam, lParam);
5632 /* test event masks in combination with WM_COMMAND */
5633 static void test_eventMask(void)
5635 HWND parent;
5636 int ret, style;
5637 WNDCLASSA cls;
5638 const char text[] = "foo bar\n";
5639 int eventMask;
5641 /* register class to capture WM_COMMAND */
5642 cls.style = 0;
5643 cls.lpfnWndProc = ParentMsgCheckProcA;
5644 cls.cbClsExtra = 0;
5645 cls.cbWndExtra = 0;
5646 cls.hInstance = GetModuleHandleA(0);
5647 cls.hIcon = 0;
5648 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5649 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5650 cls.lpszMenuName = NULL;
5651 cls.lpszClassName = "EventMaskParentClass";
5652 if(!RegisterClassA(&cls)) assert(0);
5654 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5655 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5656 ok (parent != 0, "Failed to create parent window\n");
5658 eventMaskEditHwnd = new_richedit(parent);
5659 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5661 eventMask = ENM_CHANGE | ENM_UPDATE;
5662 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5663 ok(ret == ENM_NONE, "wrong event mask\n");
5664 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5665 ok(ret == eventMask, "failed to set event mask\n");
5667 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5668 queriedEventMask = 0; /* initialize to something other than we expect */
5669 watchForEventMask = EN_CHANGE;
5670 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5671 ok(ret == TRUE, "failed to set text\n");
5672 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5673 notification in response to WM_SETTEXT */
5674 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5675 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5677 /* check to see if EN_CHANGE is sent when redraw is turned off */
5678 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5679 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5680 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5681 /* redraw is disabled by making the window invisible. */
5682 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5683 queriedEventMask = 0; /* initialize to something other than we expect */
5684 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5685 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5686 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5687 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5688 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5690 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5691 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5692 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5693 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5694 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5695 watchForEventMask = EN_UPDATE;
5696 queriedEventMask = 0; /* initialize to something other than we expect */
5697 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5698 ok(queriedEventMask == 0,
5699 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5700 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5701 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5702 queriedEventMask = 0; /* initialize to something other than we expect */
5703 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5704 ok(queriedEventMask == eventMask,
5705 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5708 DestroyWindow(parent);
5711 static int received_WM_NOTIFY = 0;
5712 static int modify_at_WM_NOTIFY = 0;
5713 static BOOL filter_on_WM_NOTIFY = FALSE;
5714 static HWND hwndRichedit_WM_NOTIFY;
5716 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5718 if(message == WM_NOTIFY)
5720 received_WM_NOTIFY = 1;
5721 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5722 if (filter_on_WM_NOTIFY) return TRUE;
5724 return DefWindowProcA(hwnd, message, wParam, lParam);
5727 static void test_WM_NOTIFY(void)
5729 HWND parent;
5730 WNDCLASSA cls;
5731 CHARFORMAT2 cf2;
5732 int sel_start, sel_end;
5734 /* register class to capture WM_NOTIFY */
5735 cls.style = 0;
5736 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5737 cls.cbClsExtra = 0;
5738 cls.cbWndExtra = 0;
5739 cls.hInstance = GetModuleHandleA(0);
5740 cls.hIcon = 0;
5741 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5742 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5743 cls.lpszMenuName = NULL;
5744 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5745 if(!RegisterClassA(&cls)) assert(0);
5747 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5748 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5749 ok (parent != 0, "Failed to create parent window\n");
5751 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5752 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5754 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5756 /* Notifications for selection change should only be sent when selection
5757 actually changes. EM_SETCHARFORMAT is one message that calls
5758 ME_CommitUndo, which should check whether message should be sent */
5759 received_WM_NOTIFY = 0;
5760 cf2.cbSize = sizeof(CHARFORMAT2);
5761 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5762 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5763 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5764 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5765 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5767 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5768 already at 0. */
5769 received_WM_NOTIFY = 0;
5770 modify_at_WM_NOTIFY = 0;
5771 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5772 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5773 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5775 received_WM_NOTIFY = 0;
5776 modify_at_WM_NOTIFY = 0;
5777 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5778 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5780 received_WM_NOTIFY = 0;
5781 modify_at_WM_NOTIFY = 0;
5782 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5783 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5784 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5786 /* Test for WM_NOTIFY messages with redraw disabled. */
5787 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5788 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5789 received_WM_NOTIFY = 0;
5790 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5791 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5792 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5794 /* Test filtering key events. */
5795 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5796 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5797 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5798 received_WM_NOTIFY = 0;
5799 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5800 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5801 ok(sel_start == 1 && sel_end == 1,
5802 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5803 filter_on_WM_NOTIFY = TRUE;
5804 received_WM_NOTIFY = 0;
5805 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5806 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5807 ok(sel_start == 1 && sel_end == 1,
5808 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5810 /* test with owner set to NULL */
5811 SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5812 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5813 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5814 ok(sel_start == 1 && sel_end == 1,
5815 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5817 DestroyWindow(hwndRichedit_WM_NOTIFY);
5818 DestroyWindow(parent);
5821 static void test_undo_coalescing(void)
5823 HWND hwnd;
5824 int result;
5825 char buffer[64] = {0};
5827 /* multi-line control inserts CR normally */
5828 if (!is_win9x)
5829 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5830 0, 0, 200, 60, 0, 0, 0, 0);
5831 else
5832 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE,
5833 0, 0, 200, 60, 0, 0, 0, 0);
5834 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5836 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5837 ok (result == FALSE, "Can undo after window creation.\n");
5838 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5839 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5840 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5841 ok (result == FALSE, "Can redo after window creation.\n");
5842 result = SendMessage(hwnd, EM_REDO, 0, 0);
5843 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5845 /* Test the effect of arrows keys during typing on undo transactions*/
5846 simulate_typing_characters(hwnd, "one two three");
5847 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5848 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5849 simulate_typing_characters(hwnd, " four five six");
5851 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5852 ok (result == FALSE, "Can redo before anything is undone.\n");
5853 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5854 ok (result == TRUE, "Cannot undo typed characters.\n");
5855 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5856 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5857 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5858 ok (result == TRUE, "Cannot redo after undo.\n");
5859 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5860 result = strcmp(buffer, "one two three");
5861 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5863 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5864 ok (result == TRUE, "Cannot undo typed characters.\n");
5865 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5866 ok (result == TRUE, "Failed to undo typed characters.\n");
5867 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5868 result = strcmp(buffer, "");
5869 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5871 /* Test the effect of focus changes during typing on undo transactions*/
5872 simulate_typing_characters(hwnd, "one two three");
5873 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5874 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5875 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5876 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5877 simulate_typing_characters(hwnd, " four five six");
5878 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5879 ok (result == TRUE, "Failed to undo typed characters.\n");
5880 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5881 result = strcmp(buffer, "one two three");
5882 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5884 /* Test the effect of the back key during typing on undo transactions */
5885 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5886 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5887 ok (result == TRUE, "Failed to clear the text.\n");
5888 simulate_typing_characters(hwnd, "one two threa");
5889 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5890 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5891 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5892 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5893 simulate_typing_characters(hwnd, "e four five six");
5894 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5895 ok (result == TRUE, "Failed to undo typed characters.\n");
5896 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5897 result = strcmp(buffer, "");
5898 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5900 /* Test the effect of the delete key during typing on undo transactions */
5901 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5902 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5903 ok(result == TRUE, "Failed to set the text.\n");
5904 SendMessage(hwnd, EM_SETSEL, 1, 1);
5905 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5906 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5907 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5908 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5909 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5910 ok (result == TRUE, "Failed to undo typed characters.\n");
5911 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5912 result = strcmp(buffer, "acd");
5913 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5914 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5915 ok (result == TRUE, "Failed to undo typed characters.\n");
5916 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5917 result = strcmp(buffer, "abcd");
5918 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5920 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5921 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5922 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5923 ok (result == TRUE, "Failed to clear the text.\n");
5924 simulate_typing_characters(hwnd, "one two three");
5925 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5926 ok (result == 0, "expected %d but got %d\n", 0, result);
5927 simulate_typing_characters(hwnd, " four five six");
5928 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5929 ok (result == TRUE, "Failed to undo typed characters.\n");
5930 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5931 result = strcmp(buffer, "one two three");
5932 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5933 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5934 ok (result == TRUE, "Failed to undo typed characters.\n");
5935 ok (result == TRUE, "Failed to undo typed characters.\n");
5936 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5937 result = strcmp(buffer, "");
5938 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5940 DestroyWindow(hwnd);
5943 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5945 int length;
5947 /* MSDN lied, length is actually the number of bytes. */
5948 length = bytes / sizeof(WCHAR);
5949 switch(code)
5951 case WB_ISDELIMITER:
5952 return text[pos] == 'X';
5953 case WB_LEFT:
5954 case WB_MOVEWORDLEFT:
5955 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5956 return pos-1;
5957 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5958 case WB_LEFTBREAK:
5959 pos--;
5960 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5961 pos--;
5962 return pos;
5963 case WB_RIGHT:
5964 case WB_MOVEWORDRIGHT:
5965 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5966 return pos+1;
5967 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5968 case WB_RIGHTBREAK:
5969 pos++;
5970 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5971 pos++;
5972 return pos;
5973 default:
5974 ok(FALSE, "Unexpected code %d\n", code);
5975 break;
5977 return 0;
5980 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5981 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5983 static void test_word_movement(void)
5985 HWND hwnd;
5986 int result;
5987 int sel_start, sel_end;
5988 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5990 /* multi-line control inserts CR normally */
5991 hwnd = new_richedit(NULL);
5993 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5994 ok (result == TRUE, "Failed to clear the text.\n");
5995 SendMessage(hwnd, EM_SETSEL, 0, 0);
5996 /* |one two three */
5998 SEND_CTRL_RIGHT(hwnd);
5999 /* one |two three */
6000 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6001 ok(sel_start == sel_end, "Selection should be empty\n");
6002 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6004 SEND_CTRL_RIGHT(hwnd);
6005 /* one two |three */
6006 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6007 ok(sel_start == sel_end, "Selection should be empty\n");
6008 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6010 SEND_CTRL_LEFT(hwnd);
6011 /* one |two three */
6012 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6013 ok(sel_start == sel_end, "Selection should be empty\n");
6014 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6016 SEND_CTRL_LEFT(hwnd);
6017 /* |one two three */
6018 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6019 ok(sel_start == sel_end, "Selection should be empty\n");
6020 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6022 SendMessage(hwnd, EM_SETSEL, 8, 8);
6023 /* one two | three */
6024 SEND_CTRL_RIGHT(hwnd);
6025 /* one two |three */
6026 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6027 ok(sel_start == sel_end, "Selection should be empty\n");
6028 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6030 SendMessage(hwnd, EM_SETSEL, 11, 11);
6031 /* one two th|ree */
6032 SEND_CTRL_LEFT(hwnd);
6033 /* one two |three */
6034 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6035 ok(sel_start == sel_end, "Selection should be empty\n");
6036 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6038 /* Test with a custom word break procedure that uses X as the delimiter. */
6039 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6040 ok (result == TRUE, "Failed to clear the text.\n");
6041 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6042 /* |one twoXthree */
6043 SEND_CTRL_RIGHT(hwnd);
6044 /* one twoX|three */
6045 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6046 ok(sel_start == sel_end, "Selection should be empty\n");
6047 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6049 DestroyWindow(hwnd);
6051 /* Make sure the behaviour is the same with a unicode richedit window,
6052 * and using unicode functions. */
6053 if (is_win9x)
6055 skip("Cannot test with unicode richedit window\n");
6056 return;
6059 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6060 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6061 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6063 /* Test with a custom word break procedure that uses X as the delimiter. */
6064 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6065 ok (result == TRUE, "Failed to clear the text.\n");
6066 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6067 /* |one twoXthree */
6068 SEND_CTRL_RIGHT(hwnd);
6069 /* one twoX|three */
6070 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6071 ok(sel_start == sel_end, "Selection should be empty\n");
6072 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6074 DestroyWindow(hwnd);
6077 static void test_EM_CHARFROMPOS(void)
6079 HWND hwnd;
6080 int result;
6081 RECT rcClient;
6082 POINTL point;
6083 point.x = 0;
6084 point.y = 40;
6086 /* multi-line control inserts CR normally */
6087 hwnd = new_richedit(NULL);
6088 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6089 (LPARAM)"one two three four five six seven\reight");
6090 ok(result == 1, "Expected 1, got %d", result);
6091 GetClientRect(hwnd, &rcClient);
6093 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6094 ok(result == 34, "expected character index of 34 but got %d\n", result);
6096 /* Test with points outside the bounds of the richedit control. */
6097 point.x = -1;
6098 point.y = 40;
6099 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6100 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6102 point.x = 1000;
6103 point.y = 0;
6104 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6105 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6107 point.x = 1000;
6108 point.y = 36;
6109 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6110 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6112 point.x = 1000;
6113 point.y = -1;
6114 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6115 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6117 point.x = 1000;
6118 point.y = rcClient.bottom + 1;
6119 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6120 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6122 point.x = 1000;
6123 point.y = rcClient.bottom;
6124 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6125 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6127 DestroyWindow(hwnd);
6130 static void test_word_wrap(void)
6132 HWND hwnd;
6133 POINTL point = {0, 60}; /* This point must be below the first line */
6134 const char *text = "Must be long enough to test line wrapping";
6135 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6136 int res, pos, lines;
6138 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6139 * when specified on window creation and set later. */
6140 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6141 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6142 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6143 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6144 ok(res, "WM_SETTEXT failed.\n");
6145 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6146 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6147 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6148 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6150 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6151 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6152 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6153 DestroyWindow(hwnd);
6155 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6156 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6157 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6159 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6160 ok(res, "WM_SETTEXT failed.\n");
6161 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6162 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6163 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6164 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6166 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6167 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6168 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6169 DestroyWindow(hwnd);
6171 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6172 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6173 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6174 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6175 ok(res, "WM_SETTEXT failed.\n");
6176 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6177 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6179 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6180 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6181 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6182 DestroyWindow(hwnd);
6184 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6185 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6186 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6187 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6188 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6189 ok(res, "WM_SETTEXT failed.\n");
6190 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6191 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6193 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6194 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6195 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6197 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6198 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6199 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6200 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6201 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6203 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6204 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6205 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6206 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6207 DestroyWindow(hwnd);
6209 /* Test to see if wrapping happens with redraw disabled. */
6210 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6211 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6212 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6213 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6214 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6215 ok(res, "EM_REPLACESEL failed.\n");
6216 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6217 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6218 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6219 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6220 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6222 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6223 DestroyWindow(hwnd);
6226 static void test_autoscroll(void)
6228 HWND hwnd = new_richedit(NULL);
6229 int lines, ret, redraw;
6230 POINT pt;
6232 for (redraw = 0; redraw <= 1; redraw++) {
6233 trace("testing with WM_SETREDRAW=%d\n", redraw);
6234 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6235 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6236 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6237 ok(lines == 8, "%d lines instead of 8\n", lines);
6238 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6239 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6240 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6241 ret = GetWindowLong(hwnd, GWL_STYLE);
6242 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6244 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6245 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6246 ok(lines == 1, "%d lines instead of 1\n", lines);
6247 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6248 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6249 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6250 ret = GetWindowLong(hwnd, GWL_STYLE);
6251 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6254 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6255 DestroyWindow(hwnd);
6257 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6258 * auto vertical/horizontal scrolling options. */
6259 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6260 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6261 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6262 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6263 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6264 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6265 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6266 ret = GetWindowLong(hwnd, GWL_STYLE);
6267 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6268 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6269 DestroyWindow(hwnd);
6271 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6272 WS_POPUP|ES_MULTILINE,
6273 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6274 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6275 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6276 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6277 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6278 ret = GetWindowLong(hwnd, GWL_STYLE);
6279 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6280 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6281 DestroyWindow(hwnd);
6285 static void test_format_rect(void)
6287 HWND hwnd;
6288 RECT rc, expected, clientRect;
6289 int n;
6290 DWORD options;
6292 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6293 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6294 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6295 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6297 GetClientRect(hwnd, &clientRect);
6299 expected = clientRect;
6300 expected.left += 1;
6301 expected.right -= 1;
6302 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6303 ok(rc.top == expected.top && rc.left == expected.left &&
6304 rc.bottom == expected.bottom && rc.right == expected.right,
6305 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6306 rc.top, rc.left, rc.bottom, rc.right,
6307 expected.top, expected.left, expected.bottom, expected.right);
6309 for (n = -3; n <= 3; n++)
6311 rc = clientRect;
6312 rc.top += n;
6313 rc.left += n;
6314 rc.bottom -= n;
6315 rc.right -= n;
6316 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6318 expected = rc;
6319 expected.top = max(0, rc.top);
6320 expected.left = max(0, rc.left);
6321 expected.bottom = min(clientRect.bottom, rc.bottom);
6322 expected.right = min(clientRect.right, rc.right);
6323 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6324 ok(rc.top == expected.top && rc.left == expected.left &&
6325 rc.bottom == expected.bottom && rc.right == expected.right,
6326 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6327 n, rc.top, rc.left, rc.bottom, rc.right,
6328 expected.top, expected.left, expected.bottom, expected.right);
6331 rc = clientRect;
6332 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6333 expected = clientRect;
6334 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6335 ok(rc.top == expected.top && rc.left == expected.left &&
6336 rc.bottom == expected.bottom && rc.right == expected.right,
6337 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6338 rc.top, rc.left, rc.bottom, rc.right,
6339 expected.top, expected.left, expected.bottom, expected.right);
6341 /* Adding the selectionbar adds the selectionbar width to the left side. */
6342 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6343 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6344 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6345 expected.left += 8; /* selection bar width */
6346 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6347 ok(rc.top == expected.top && rc.left == expected.left &&
6348 rc.bottom == expected.bottom && rc.right == expected.right,
6349 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6350 rc.top, rc.left, rc.bottom, rc.right,
6351 expected.top, expected.left, expected.bottom, expected.right);
6353 rc = clientRect;
6354 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6355 expected = clientRect;
6356 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6357 ok(rc.top == expected.top && rc.left == expected.left &&
6358 rc.bottom == expected.bottom && rc.right == expected.right,
6359 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6360 rc.top, rc.left, rc.bottom, rc.right,
6361 expected.top, expected.left, expected.bottom, expected.right);
6363 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6364 * even if the left side is already 0. */
6365 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6366 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6367 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6368 expected.left -= 8; /* selection bar width */
6369 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6370 ok(rc.top == expected.top && rc.left == expected.left &&
6371 rc.bottom == expected.bottom && rc.right == expected.right,
6372 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6373 rc.top, rc.left, rc.bottom, rc.right,
6374 expected.top, expected.left, expected.bottom, expected.right);
6376 /* Set the absolute value of the formatting rectangle. */
6377 rc = clientRect;
6378 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6379 expected = clientRect;
6380 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6381 ok(rc.top == expected.top && rc.left == expected.left &&
6382 rc.bottom == expected.bottom && rc.right == expected.right,
6383 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6384 n, rc.top, rc.left, rc.bottom, rc.right,
6385 expected.top, expected.left, expected.bottom, expected.right);
6387 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6388 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6389 * tests show that this isn't true. */
6390 rc.top = 15;
6391 rc.left = 15;
6392 rc.bottom = clientRect.bottom - 15;
6393 rc.right = clientRect.right - 15;
6394 expected = rc;
6395 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6396 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6397 ok(rc.top == expected.top && rc.left == expected.left &&
6398 rc.bottom == expected.bottom && rc.right == expected.right,
6399 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6400 rc.top, rc.left, rc.bottom, rc.right,
6401 expected.top, expected.left, expected.bottom, expected.right);
6403 /* For some reason it does not limit the values to the client rect with
6404 * a WPARAM value of 1. */
6405 rc.top = -15;
6406 rc.left = -15;
6407 rc.bottom = clientRect.bottom + 15;
6408 rc.right = clientRect.right + 15;
6409 expected = rc;
6410 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6411 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6412 ok(rc.top == expected.top && rc.left == expected.left &&
6413 rc.bottom == expected.bottom && rc.right == expected.right,
6414 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6415 rc.top, rc.left, rc.bottom, rc.right,
6416 expected.top, expected.left, expected.bottom, expected.right);
6418 DestroyWindow(hwnd);
6420 /* The extended window style affects the formatting rectangle. */
6421 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6422 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6423 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6424 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6426 GetClientRect(hwnd, &clientRect);
6428 expected = clientRect;
6429 expected.left += 1;
6430 expected.top += 1;
6431 expected.right -= 1;
6432 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6433 ok(rc.top == expected.top && rc.left == expected.left &&
6434 rc.bottom == expected.bottom && rc.right == expected.right,
6435 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6436 rc.top, rc.left, rc.bottom, rc.right,
6437 expected.top, expected.left, expected.bottom, expected.right);
6439 rc = clientRect;
6440 rc.top += 5;
6441 rc.left += 5;
6442 rc.bottom -= 5;
6443 rc.right -= 5;
6444 expected = rc;
6445 expected.top -= 1;
6446 expected.left -= 1;
6447 expected.right += 1;
6448 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6449 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6450 ok(rc.top == expected.top && rc.left == expected.left &&
6451 rc.bottom == expected.bottom && rc.right == expected.right,
6452 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6453 rc.top, rc.left, rc.bottom, rc.right,
6454 expected.top, expected.left, expected.bottom, expected.right);
6456 DestroyWindow(hwnd);
6459 static void test_WM_GETDLGCODE(void)
6461 HWND hwnd;
6462 UINT res, expected;
6463 MSG msg;
6465 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6467 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6468 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6469 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6470 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6471 msg.hwnd = hwnd;
6472 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6473 expected = expected | DLGC_WANTMESSAGE;
6474 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6475 res, expected);
6476 DestroyWindow(hwnd);
6478 msg.message = WM_KEYDOWN;
6479 msg.wParam = VK_RETURN;
6480 msg.lParam = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) | 0x0001;
6481 msg.pt.x = 0;
6482 msg.pt.y = 0;
6483 msg.time = GetTickCount();
6485 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6486 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6487 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6488 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6489 msg.hwnd = hwnd;
6490 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6491 expected = expected | DLGC_WANTMESSAGE;
6492 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6493 res, expected);
6494 DestroyWindow(hwnd);
6496 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6497 ES_MULTILINE|WS_POPUP,
6498 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6499 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6500 msg.hwnd = hwnd;
6501 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6502 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6503 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6504 res, expected);
6505 DestroyWindow(hwnd);
6507 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6508 ES_WANTRETURN|WS_POPUP,
6509 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6510 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6511 msg.hwnd = hwnd;
6512 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6513 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6514 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6515 res, expected);
6516 DestroyWindow(hwnd);
6518 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6519 WS_POPUP,
6520 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6521 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6522 msg.hwnd = hwnd;
6523 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6524 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6525 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6526 res, expected);
6527 DestroyWindow(hwnd);
6529 msg.wParam = VK_TAB;
6530 msg.lParam = MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) | 0x0001;
6532 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6533 ES_MULTILINE|WS_POPUP,
6534 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6535 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6536 msg.hwnd = hwnd;
6537 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6538 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6539 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6540 res, expected);
6541 DestroyWindow(hwnd);
6543 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6544 WS_POPUP,
6545 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6546 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6547 msg.hwnd = hwnd;
6548 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6549 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6550 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6551 res, expected);
6552 DestroyWindow(hwnd);
6554 hold_key(VK_CONTROL);
6556 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6557 ES_MULTILINE|WS_POPUP,
6558 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6559 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6560 msg.hwnd = hwnd;
6561 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6562 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6563 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6564 res, expected);
6565 DestroyWindow(hwnd);
6567 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6568 WS_POPUP,
6569 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6570 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6571 msg.hwnd = hwnd;
6572 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6573 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6574 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6575 res, expected);
6576 DestroyWindow(hwnd);
6578 release_key(VK_CONTROL);
6580 msg.wParam = 'a';
6581 msg.lParam = MapVirtualKey('a', MAPVK_VK_TO_VSC) | 0x0001;
6583 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6584 ES_MULTILINE|WS_POPUP,
6585 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6586 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6587 msg.hwnd = hwnd;
6588 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6589 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6590 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6591 res, expected);
6592 DestroyWindow(hwnd);
6594 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6595 WS_POPUP,
6596 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6597 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6598 msg.hwnd = hwnd;
6599 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6600 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6601 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6602 res, expected);
6603 DestroyWindow(hwnd);
6605 msg.message = WM_CHAR;
6607 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6608 ES_MULTILINE|WS_POPUP,
6609 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6610 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6611 msg.hwnd = hwnd;
6612 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6613 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6614 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6615 res, expected);
6616 DestroyWindow(hwnd);
6618 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6619 WS_POPUP,
6620 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6621 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6622 msg.hwnd = hwnd;
6623 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6624 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6625 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6626 res, expected);
6627 DestroyWindow(hwnd);
6630 static void test_zoom(void)
6632 HWND hwnd;
6633 UINT ret;
6634 RECT rc;
6635 POINT pt;
6636 int numerator, denominator;
6638 hwnd = new_richedit(NULL);
6639 GetClientRect(hwnd, &rc);
6640 pt.x = (rc.right - rc.left) / 2;
6641 pt.y = (rc.bottom - rc.top) / 2;
6642 ClientToScreen(hwnd, &pt);
6644 /* Test initial zoom value */
6645 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6646 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6647 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6648 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6650 /* test scroll wheel */
6651 hold_key(VK_CONTROL);
6652 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6653 MAKELPARAM(pt.x, pt.y));
6654 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6655 release_key(VK_CONTROL);
6657 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6658 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6659 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6660 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6662 /* Test how much the mouse wheel can zoom in and out. */
6663 ret = SendMessage(hwnd, EM_SETZOOM, 490, 100);
6664 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6666 hold_key(VK_CONTROL);
6667 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6668 MAKELPARAM(pt.x, pt.y));
6669 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6670 release_key(VK_CONTROL);
6672 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6673 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6674 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6675 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6677 ret = SendMessage(hwnd, EM_SETZOOM, 491, 100);
6678 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6680 hold_key(VK_CONTROL);
6681 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6682 MAKELPARAM(pt.x, pt.y));
6683 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6684 release_key(VK_CONTROL);
6686 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6687 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6688 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6689 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6691 ret = SendMessage(hwnd, EM_SETZOOM, 20, 100);
6692 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6694 hold_key(VK_CONTROL);
6695 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6696 MAKELPARAM(pt.x, pt.y));
6697 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6698 release_key(VK_CONTROL);
6700 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6701 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6702 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6703 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6705 ret = SendMessage(hwnd, EM_SETZOOM, 19, 100);
6706 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6708 hold_key(VK_CONTROL);
6709 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6710 MAKELPARAM(pt.x, pt.y));
6711 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6712 release_key(VK_CONTROL);
6714 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6715 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6716 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6717 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6719 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6720 ret = SendMessage(hwnd, EM_SETZOOM, 50, 13);
6721 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6723 hold_key(VK_CONTROL);
6724 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6725 MAKELPARAM(pt.x, pt.y));
6726 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6727 release_key(VK_CONTROL);
6729 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6730 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6731 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6732 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6734 /* Test bounds checking on EM_SETZOOM */
6735 ret = SendMessage(hwnd, EM_SETZOOM, 2, 127);
6736 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6738 ret = SendMessage(hwnd, EM_SETZOOM, 127, 2);
6739 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6741 ret = SendMessage(hwnd, EM_SETZOOM, 2, 128);
6742 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6744 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6745 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6746 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6747 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6749 ret = SendMessage(hwnd, EM_SETZOOM, 128, 2);
6750 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6752 /* See if negative numbers are accepted. */
6753 ret = SendMessage(hwnd, EM_SETZOOM, -100, -100);
6754 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6756 /* See if negative numbers are accepted. */
6757 ret = SendMessage(hwnd, EM_SETZOOM, 0, 100);
6758 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6760 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6761 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6762 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6763 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6765 /* Reset the zoom value */
6766 ret = SendMessage(hwnd, EM_SETZOOM, 0, 0);
6767 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6769 DestroyWindow(hwnd);
6772 struct dialog_mode_messages
6774 int wm_getdefid, wm_close, wm_nextdlgctl;
6777 static struct dialog_mode_messages dm_messages;
6779 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6780 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6781 "got %d\n", wmclose, dm_messages.wm_close); \
6782 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6783 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6784 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6785 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6787 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6789 switch (iMsg)
6791 case DM_GETDEFID:
6792 dm_messages.wm_getdefid++;
6793 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6794 case WM_NEXTDLGCTL:
6795 dm_messages.wm_nextdlgctl++;
6796 break;
6797 case WM_CLOSE:
6798 dm_messages.wm_close++;
6799 break;
6802 return DefWindowProc(hwnd, iMsg, wParam, lParam);
6805 static void test_dialogmode(void)
6807 HWND hwRichEdit, hwParent, hwButton;
6808 MSG msg= {0};
6809 int lcount, r;
6810 WNDCLASSA cls;
6812 cls.style = 0;
6813 cls.lpfnWndProc = dialog_mode_wnd_proc;
6814 cls.cbClsExtra = 0;
6815 cls.cbWndExtra = 0;
6816 cls.hInstance = GetModuleHandleA(0);
6817 cls.hIcon = 0;
6818 cls.hCursor = LoadCursorA(0, IDC_ARROW);
6819 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6820 cls.lpszMenuName = NULL;
6821 cls.lpszClassName = "DialogModeParentClass";
6822 if(!RegisterClassA(&cls)) assert(0);
6824 hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6825 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6827 /* Test richedit(ES_MULTILINE) */
6829 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6831 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6832 ok(0 == r, "expected 0, got %d\n", r);
6833 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6834 ok(2 == lcount, "expected 2, got %d\n", lcount);
6836 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6837 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6839 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6840 ok(0 == r, "expected 0, got %d\n", r);
6841 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6842 ok(3 == lcount, "expected 3, got %d\n", lcount);
6844 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6845 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6846 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6847 ok(0 == r, "expected 0, got %d\n", r);
6848 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6849 ok(3 == lcount, "expected 3, got %d\n", lcount);
6851 DestroyWindow(hwRichEdit);
6853 /* Test standalone richedit(ES_MULTILINE) */
6855 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6857 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6858 ok(0 == r, "expected 0, got %d\n", r);
6859 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6860 ok(2 == lcount, "expected 2, got %d\n", lcount);
6862 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6863 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6865 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6866 ok(0 == r, "expected 0, got %d\n", r);
6867 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6868 ok(2 == lcount, "expected 2, got %d\n", lcount);
6870 DestroyWindow(hwRichEdit);
6872 /* Check a destination for messages */
6874 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6876 SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6877 SetParent( hwRichEdit, NULL);
6879 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6880 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6882 memset(&dm_messages, 0, sizeof(dm_messages));
6883 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6884 ok(0 == r, "expected 0, got %d\n", r);
6885 test_dm_messages(0, 1, 0);
6887 memset(&dm_messages, 0, sizeof(dm_messages));
6888 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6889 ok(0 == r, "expected 0, got %d\n", r);
6890 test_dm_messages(0, 0, 1);
6892 DestroyWindow(hwRichEdit);
6894 /* Check messages from richedit(ES_MULTILINE) */
6896 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6898 memset(&dm_messages, 0, sizeof(dm_messages));
6899 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6900 ok(0 == r, "expected 0, got %d\n", r);
6901 test_dm_messages(0, 0, 0);
6903 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6904 ok(2 == lcount, "expected 2, got %d\n", lcount);
6906 memset(&dm_messages, 0, sizeof(dm_messages));
6907 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6908 ok(0 == r, "expected 0, got %d\n", r);
6909 test_dm_messages(0, 0, 0);
6911 memset(&dm_messages, 0, sizeof(dm_messages));
6912 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6913 ok(0 == r, "expected 0, got %d\n", r);
6914 test_dm_messages(0, 0, 0);
6916 memset(&dm_messages, 0, sizeof(dm_messages));
6917 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6918 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6919 test_dm_messages(0, 0, 0);
6921 memset(&dm_messages, 0, sizeof(dm_messages));
6922 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6923 ok(0 == r, "expected 0, got %d\n", r);
6924 test_dm_messages(0, 1, 0);
6926 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6927 ok(2 == lcount, "expected 2, got %d\n", lcount);
6929 memset(&dm_messages, 0, sizeof(dm_messages));
6930 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6931 ok(0 == r, "expected 0, got %d\n", r);
6932 test_dm_messages(0, 0, 0);
6934 memset(&dm_messages, 0, sizeof(dm_messages));
6935 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6936 ok(0 == r, "expected 0, got %d\n", r);
6937 test_dm_messages(0, 0, 1);
6939 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6940 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6941 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6943 memset(&dm_messages, 0, sizeof(dm_messages));
6944 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6945 ok(0 == r, "expected 0, got %d\n", r);
6946 test_dm_messages(0, 1, 1);
6948 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6949 ok(2 == lcount, "expected 2, got %d\n", lcount);
6951 DestroyWindow(hwButton);
6952 DestroyWindow(hwRichEdit);
6954 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
6956 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, hwParent);
6958 memset(&dm_messages, 0, sizeof(dm_messages));
6959 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6960 ok(0 == r, "expected 0, got %d\n", r);
6961 test_dm_messages(0, 0, 0);
6963 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6964 ok(2 == lcount, "expected 2, got %d\n", lcount);
6966 memset(&dm_messages, 0, sizeof(dm_messages));
6967 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6968 ok(0 == r, "expected 0, got %d\n", r);
6969 test_dm_messages(0, 0, 0);
6971 memset(&dm_messages, 0, sizeof(dm_messages));
6972 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6973 ok(0 == r, "expected 0, got %d\n", r);
6974 test_dm_messages(0, 0, 0);
6976 memset(&dm_messages, 0, sizeof(dm_messages));
6977 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6978 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6979 test_dm_messages(0, 0, 0);
6981 memset(&dm_messages, 0, sizeof(dm_messages));
6982 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6983 ok(0 == r, "expected 0, got %d\n", r);
6984 test_dm_messages(0, 0, 0);
6986 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6987 ok(3 == lcount, "expected 3, got %d\n", lcount);
6989 memset(&dm_messages, 0, sizeof(dm_messages));
6990 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6991 ok(0 == r, "expected 0, got %d\n", r);
6992 test_dm_messages(0, 0, 0);
6994 memset(&dm_messages, 0, sizeof(dm_messages));
6995 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6996 ok(0 == r, "expected 0, got %d\n", r);
6997 test_dm_messages(0, 0, 1);
6999 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7000 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7001 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7003 memset(&dm_messages, 0, sizeof(dm_messages));
7004 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7005 ok(0 == r, "expected 0, got %d\n", r);
7006 test_dm_messages(0, 0, 0);
7008 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7009 ok(4 == lcount, "expected 4, got %d\n", lcount);
7011 DestroyWindow(hwButton);
7012 DestroyWindow(hwRichEdit);
7014 /* Check messages from richedit(0) */
7016 hwRichEdit = new_window(RICHEDIT_CLASS, 0, hwParent);
7018 memset(&dm_messages, 0, sizeof(dm_messages));
7019 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7020 ok(0 == r, "expected 0, got %d\n", r);
7021 test_dm_messages(0, 0, 0);
7023 memset(&dm_messages, 0, sizeof(dm_messages));
7024 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7025 ok(0 == r, "expected 0, got %d\n", r);
7026 test_dm_messages(0, 0, 0);
7028 memset(&dm_messages, 0, sizeof(dm_messages));
7029 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7030 ok(0 == r, "expected 0, got %d\n", r);
7031 test_dm_messages(0, 0, 0);
7033 memset(&dm_messages, 0, sizeof(dm_messages));
7034 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7035 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7036 test_dm_messages(0, 0, 0);
7038 memset(&dm_messages, 0, sizeof(dm_messages));
7039 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7040 ok(0 == r, "expected 0, got %d\n", r);
7041 test_dm_messages(0, 1, 0);
7043 memset(&dm_messages, 0, sizeof(dm_messages));
7044 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7045 ok(0 == r, "expected 0, got %d\n", r);
7046 test_dm_messages(0, 0, 0);
7048 memset(&dm_messages, 0, sizeof(dm_messages));
7049 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7050 ok(0 == r, "expected 0, got %d\n", r);
7051 test_dm_messages(0, 0, 1);
7053 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7054 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7055 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7057 memset(&dm_messages, 0, sizeof(dm_messages));
7058 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7059 ok(0 == r, "expected 0, got %d\n", r);
7060 test_dm_messages(0, 1, 1);
7062 DestroyWindow(hwRichEdit);
7064 /* Check messages from richedit(ES_WANTRETURN) */
7066 hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7068 memset(&dm_messages, 0, sizeof(dm_messages));
7069 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7070 ok(0 == r, "expected 0, got %d\n", r);
7071 test_dm_messages(0, 0, 0);
7073 memset(&dm_messages, 0, sizeof(dm_messages));
7074 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7075 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7076 test_dm_messages(0, 0, 0);
7078 memset(&dm_messages, 0, sizeof(dm_messages));
7079 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7080 ok(0 == r, "expected 0, got %d\n", r);
7081 test_dm_messages(0, 0, 0);
7083 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7084 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7085 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7087 memset(&dm_messages, 0, sizeof(dm_messages));
7088 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7089 ok(0 == r, "expected 0, got %d\n", r);
7090 test_dm_messages(0, 0, 0);
7092 DestroyWindow(hwRichEdit);
7093 DestroyWindow(hwParent);
7096 START_TEST( editor )
7098 BOOL ret;
7099 /* Must explicitly LoadLibrary(). The test has no references to functions in
7100 * RICHED20.DLL, so the linker doesn't actually link to it. */
7101 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
7102 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7104 is_win9x = GetVersion() & 0x80000000;
7106 test_WM_CHAR();
7107 test_EM_FINDTEXT();
7108 test_EM_GETLINE();
7109 test_EM_POSFROMCHAR();
7110 test_EM_SCROLLCARET();
7111 test_EM_SCROLL();
7112 test_scrollbar_visibility();
7113 test_WM_SETTEXT();
7114 test_EM_LINELENGTH();
7115 test_EM_SETCHARFORMAT();
7116 test_EM_SETTEXTMODE();
7117 test_TM_PLAINTEXT();
7118 test_EM_SETOPTIONS();
7119 test_WM_GETTEXT();
7120 test_EM_GETTEXTRANGE();
7121 test_EM_GETSELTEXT();
7122 test_EM_SETUNDOLIMIT();
7123 test_ES_PASSWORD();
7124 test_EM_SETTEXTEX();
7125 test_EM_LIMITTEXT();
7126 test_EM_EXLIMITTEXT();
7127 test_EM_GETLIMITTEXT();
7128 test_WM_SETFONT();
7129 test_EM_GETMODIFY();
7130 test_EM_EXSETSEL();
7131 test_WM_PASTE();
7132 test_EM_STREAMIN();
7133 test_EM_STREAMOUT();
7134 test_EM_STREAMOUT_FONTTBL();
7135 test_EM_StreamIn_Undo();
7136 test_EM_FORMATRANGE();
7137 test_unicode_conversions();
7138 test_EM_GETTEXTLENGTHEX();
7139 test_EM_REPLACESEL(1);
7140 test_EM_REPLACESEL(0);
7141 test_WM_NOTIFY();
7142 test_EM_AUTOURLDETECT();
7143 test_eventMask();
7144 test_undo_coalescing();
7145 test_word_movement();
7146 test_EM_CHARFROMPOS();
7147 test_SETPARAFORMAT();
7148 test_word_wrap();
7149 test_autoscroll();
7150 test_format_rect();
7151 test_WM_GETDLGCODE();
7152 test_zoom();
7153 test_dialogmode();
7155 /* Set the environment variable WINETEST_RICHED20 to keep windows
7156 * responsive and open for 30 seconds. This is useful for debugging.
7158 if (getenv( "WINETEST_RICHED20" )) {
7159 keep_responsive(30);
7162 OleFlushClipboard();
7163 ret = FreeLibrary(hmoduleRichEdit);
7164 ok(ret, "error: %d\n", (int) GetLastError());