riched20/tests: Test format rect adaption to window size and behavior with zero-sized...
[wine.git] / dlls / riched20 / tests / editor.c
blob827f35dffe4994e8a9da82b7a03aed040530ec1a
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 HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
50 HWND hwnd;
51 hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
52 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
53 hmoduleRichEdit, NULL);
54 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
55 return hwnd;
58 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
59 HWND hwnd;
60 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
61 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
62 hmoduleRichEdit, NULL);
63 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
64 return hwnd;
67 static HWND new_richedit(HWND parent) {
68 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
71 static HWND new_richeditW(HWND parent) {
72 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
75 /* Keeps the window reponsive for the deley_time in seconds.
76 * This is useful for debugging a test to see what is happening. */
77 static void keep_responsive(time_t delay_time)
79 MSG msg;
80 time_t end;
82 /* The message pump uses PeekMessage() to empty the queue and then
83 * sleeps for 50ms before retrying the queue. */
84 end = time(NULL) + delay_time;
85 while (time(NULL) < end) {
86 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
87 TranslateMessage(&msg);
88 DispatchMessage(&msg);
89 } else {
90 Sleep(50);
95 static void simulate_typing_characters(HWND hwnd, const char* szChars)
97 int ret;
99 while (*szChars != '\0') {
100 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
101 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
102 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
103 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
104 szChars++;
108 static BOOL hold_key(int vk)
110 BYTE key_state[256];
111 BOOL result;
113 result = GetKeyboardState(key_state);
114 ok(result, "GetKeyboardState failed.\n");
115 if (!result) return FALSE;
116 key_state[vk] |= 0x80;
117 result = SetKeyboardState(key_state);
118 ok(result, "SetKeyboardState failed.\n");
119 return result != 0;
122 static BOOL release_key(int vk)
124 BYTE key_state[256];
125 BOOL result;
127 result = GetKeyboardState(key_state);
128 ok(result, "GetKeyboardState failed.\n");
129 if (!result) return FALSE;
130 key_state[vk] &= ~0x80;
131 result = SetKeyboardState(key_state);
132 ok(result, "SetKeyboardState failed.\n");
133 return result != 0;
136 static const char haystack[] = "WINEWine wineWine wine WineWine";
137 /* ^0 ^10 ^20 ^30 */
139 struct find_s {
140 int start;
141 int end;
142 const char *needle;
143 int flags;
144 int expected_loc;
148 static struct find_s find_tests[] = {
149 /* Find in empty text */
150 {0, -1, "foo", FR_DOWN, -1},
151 {0, -1, "foo", 0, -1},
152 {0, -1, "", FR_DOWN, -1},
153 {20, 5, "foo", FR_DOWN, -1},
154 {5, 20, "foo", FR_DOWN, -1}
157 static struct find_s find_tests2[] = {
158 /* No-result find */
159 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
160 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
162 /* Subsequent finds */
163 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
164 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
165 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
166 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
168 /* Find backwards */
169 {19, 20, "Wine", FR_MATCHCASE, 13},
170 {10, 20, "Wine", FR_MATCHCASE, 4},
171 {20, 10, "Wine", FR_MATCHCASE, 13},
173 /* Case-insensitive */
174 {1, 31, "wInE", FR_DOWN, 4},
175 {1, 31, "Wine", FR_DOWN, 4},
177 /* High-to-low ranges */
178 {20, 5, "Wine", FR_DOWN, -1},
179 {2, 1, "Wine", FR_DOWN, -1},
180 {30, 29, "Wine", FR_DOWN, -1},
181 {20, 5, "Wine", 0, 13},
183 /* Find nothing */
184 {5, 10, "", FR_DOWN, -1},
185 {10, 5, "", FR_DOWN, -1},
186 {0, -1, "", FR_DOWN, -1},
187 {10, 5, "", 0, -1},
189 /* Whole-word search */
190 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
191 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
192 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
193 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
194 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
195 {11, -1, "winewine", FR_WHOLEWORD, 0},
196 {31, -1, "winewine", FR_WHOLEWORD, 23},
198 /* Bad ranges */
199 {5, 200, "XXX", FR_DOWN, -1},
200 {-20, 20, "Wine", FR_DOWN, -1},
201 {-20, 20, "Wine", FR_DOWN, -1},
202 {-15, -20, "Wine", FR_DOWN, -1},
203 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
205 /* Check the case noted in bug 4479 where matches at end aren't recognized */
206 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
207 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
208 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
209 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
210 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
212 /* The backwards case of bug 4479; bounds look right
213 * Fails because backward find is wrong */
214 {19, 20, "WINE", FR_MATCHCASE, 0},
215 {0, 20, "WINE", FR_MATCHCASE, -1},
217 {0, -1, "wineWine wine", 0, -1},
220 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
221 int findloc;
222 FINDTEXT ft;
223 memset(&ft, 0, sizeof(ft));
224 ft.chrg.cpMin = f->start;
225 ft.chrg.cpMax = f->end;
226 ft.lpstrText = f->needle;
227 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
228 ok(findloc == f->expected_loc,
229 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
230 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
233 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
234 int id) {
235 int findloc;
236 FINDTEXTEX ft;
237 int expected_end_loc;
239 memset(&ft, 0, sizeof(ft));
240 ft.chrg.cpMin = f->start;
241 ft.chrg.cpMax = f->end;
242 ft.lpstrText = f->needle;
243 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
244 ok(findloc == f->expected_loc,
245 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
246 name, id, f->needle, f->start, f->end, f->flags, findloc);
247 ok(ft.chrgText.cpMin == f->expected_loc,
248 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
249 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
250 expected_end_loc = ((f->expected_loc == -1) ? -1
251 : f->expected_loc + strlen(f->needle));
252 ok(ft.chrgText.cpMax == expected_end_loc,
253 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
254 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
257 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
258 int num_tests)
260 int i;
262 for (i = 0; i < num_tests; i++) {
263 check_EM_FINDTEXT(hwnd, name, &find[i], i);
264 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
268 static void test_EM_FINDTEXT(void)
270 HWND hwndRichEdit = new_richedit(NULL);
271 CHARFORMAT2 cf2;
273 /* Empty rich edit control */
274 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
275 sizeof(find_tests)/sizeof(struct find_s));
277 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
279 /* Haystack text */
280 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
281 sizeof(find_tests2)/sizeof(struct find_s));
283 /* Setting a format on an arbitrary range should have no effect in search
284 results. This tests correct offset reporting across runs. */
285 cf2.cbSize = sizeof(CHARFORMAT2);
286 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
287 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
288 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
289 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
290 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
292 /* Haystack text, again */
293 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
294 sizeof(find_tests2)/sizeof(struct find_s));
296 /* Yet another range */
297 cf2.dwMask = CFM_BOLD | cf2.dwMask;
298 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
299 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
300 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
302 /* Haystack text, again */
303 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
304 sizeof(find_tests2)/sizeof(struct find_s));
306 DestroyWindow(hwndRichEdit);
309 static const struct getline_s {
310 int line;
311 size_t buffer_len;
312 const char *text;
313 } gl[] = {
314 {0, 10, "foo bar\r"},
315 {1, 10, "\r"},
316 {2, 10, "bar\r"},
317 {3, 10, "\r"},
319 /* Buffer smaller than line length */
320 {0, 2, "foo bar\r"},
321 {0, 1, "foo bar\r"},
322 {0, 0, "foo bar\r"}
325 static void test_EM_GETLINE(void)
327 int i;
328 HWND hwndRichEdit = new_richedit(NULL);
329 static const int nBuf = 1024;
330 char dest[1024], origdest[1024];
331 const char text[] = "foo bar\n"
332 "\n"
333 "bar\n";
335 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
337 memset(origdest, 0xBB, nBuf);
338 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
340 int nCopied;
341 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
342 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
343 memset(dest, 0xBB, nBuf);
344 *(WORD *) dest = gl[i].buffer_len;
346 /* EM_GETLINE appends a "\r\0" to the end of the line
347 * nCopied counts up to and including the '\r' */
348 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
349 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
350 expected_nCopied);
351 /* two special cases since a parameter is passed via dest */
352 if (gl[i].buffer_len == 0)
353 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
354 "buffer_len=0\n");
355 else if (gl[i].buffer_len == 1)
356 ok(dest[0] == gl[i].text[0] && !dest[1] &&
357 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
358 else
360 /* Prepare hex strings of buffers to dump on failure. */
361 char expectedbuf[1024];
362 char resultbuf[1024];
363 int j;
364 resultbuf[0] = '\0';
365 for (j = 0; j < 32; j++)
366 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
367 expectedbuf[0] = '\0';
368 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
369 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
370 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
371 sprintf(expectedbuf+strlen(expectedbuf), "??");
372 for (; j < 32; j++) /* Bytes after declared buffer size */
373 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
375 /* Test the part of the buffer that is expected to be written according
376 * to the MSDN documentation fo EM_GETLINE, which does not state that
377 * a NULL terminating character will be added unless no text is copied.
379 * Windows NT does not append a NULL terminating character, but
380 * Windows 2000 and up do append a NULL terminating character if there
381 * is space in the buffer. The test will ignore this difference. */
382 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
383 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
384 i, expected_bytes_written, expectedbuf, resultbuf);
385 /* Test the part of the buffer after the declared length to make sure
386 * there are no buffer overruns. */
387 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
388 nBuf - gl[i].buffer_len),
389 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
390 i, expected_bytes_written, expectedbuf, resultbuf);
394 DestroyWindow(hwndRichEdit);
397 static void test_EM_LINELENGTH(void)
399 HWND hwndRichEdit = new_richedit(NULL);
400 const char * text =
401 "richedit1\r"
402 "richedit1\n"
403 "richedit1\r\n"
404 "richedit1";
405 int offset_test[10][2] = {
406 {0, 9},
407 {5, 9},
408 {10, 9},
409 {15, 9},
410 {20, 9},
411 {25, 9},
412 {30, 9},
413 {35, 9},
414 {40, 0},
415 {45, 0},
417 int i;
418 LRESULT result;
420 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
422 for (i = 0; i < 10; i++) {
423 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
424 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
425 offset_test[i][0], result, offset_test[i][1]);
428 DestroyWindow(hwndRichEdit);
431 static int get_scroll_pos_y(HWND hwnd)
433 POINT p = {-1, -1};
434 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
435 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
436 return p.y;
439 static void move_cursor(HWND hwnd, LONG charindex)
441 CHARRANGE cr;
442 cr.cpMax = charindex;
443 cr.cpMin = charindex;
444 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
447 static void line_scroll(HWND hwnd, int amount)
449 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
452 static void test_EM_SCROLLCARET(void)
454 int prevY, curY;
455 const char text[] = "aa\n"
456 "this is a long line of text that should be longer than the "
457 "control's width\n"
458 "cc\n"
459 "dd\n"
460 "ee\n"
461 "ff\n"
462 "gg\n"
463 "hh\n";
464 /* The richedit window height needs to be large enough vertically to fit in
465 * more than two lines of text, so the new_richedit function can't be used
466 * since a height of 60 was not large enough on some systems.
468 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
469 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
470 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
471 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
473 /* Can't verify this */
474 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
476 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
478 /* Caret above visible window */
479 line_scroll(hwndRichEdit, 3);
480 prevY = get_scroll_pos_y(hwndRichEdit);
481 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
482 curY = get_scroll_pos_y(hwndRichEdit);
483 ok(prevY != curY, "%d == %d\n", prevY, curY);
485 /* Caret below visible window */
486 move_cursor(hwndRichEdit, sizeof(text) - 1);
487 line_scroll(hwndRichEdit, -3);
488 prevY = get_scroll_pos_y(hwndRichEdit);
489 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
490 curY = get_scroll_pos_y(hwndRichEdit);
491 ok(prevY != curY, "%d == %d\n", prevY, curY);
493 /* Caret in visible window */
494 move_cursor(hwndRichEdit, sizeof(text) - 2);
495 prevY = get_scroll_pos_y(hwndRichEdit);
496 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
497 curY = get_scroll_pos_y(hwndRichEdit);
498 ok(prevY == curY, "%d != %d\n", prevY, curY);
500 /* Caret still in visible window */
501 line_scroll(hwndRichEdit, -1);
502 prevY = get_scroll_pos_y(hwndRichEdit);
503 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
504 curY = get_scroll_pos_y(hwndRichEdit);
505 ok(prevY == curY, "%d != %d\n", prevY, curY);
507 DestroyWindow(hwndRichEdit);
510 static void test_EM_POSFROMCHAR(void)
512 HWND hwndRichEdit = new_richedit(NULL);
513 int i, expected;
514 LRESULT result;
515 unsigned int height = 0;
516 int xpos = 0;
517 POINTL pt;
518 LOCALESIGNATURE sig;
519 BOOL rtl;
520 static const char text[] = "aa\n"
521 "this is a long line of text that should be longer than the "
522 "control's width\n"
523 "cc\n"
524 "dd\n"
525 "ee\n"
526 "ff\n"
527 "gg\n"
528 "hh\n";
530 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
531 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
532 (sig.lsUsb[3] & 0x08000000) != 0);
534 /* Fill the control to lines to ensure that most of them are offscreen */
535 for (i = 0; i < 50; i++)
537 /* Do not modify the string; it is exactly 16 characters long. */
538 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
539 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
543 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
544 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
545 Richedit 3.0 accepts either of the above API conventions.
548 /* Testing Richedit 2.0 API format */
550 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
551 Since all lines are identical and drawn with the same font,
552 they should have the same height... right?
554 for (i = 0; i < 50; i++)
556 /* All the lines are 16 characters long */
557 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
558 if (i == 0)
560 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
561 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
562 xpos = LOWORD(result);
564 else if (i == 1)
566 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
567 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
568 height = HIWORD(result);
570 else
572 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
573 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
577 /* Testing position at end of text */
578 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
579 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
580 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
582 /* Testing position way past end of text */
583 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
584 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
585 expected = (rtl ? 8 : 1);
586 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
588 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
589 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
590 for (i = 0; i < 50; i++)
592 /* All the lines are 16 characters long */
593 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
594 ok((signed short)(HIWORD(result)) == (i - 1) * height,
595 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
596 (signed short)(HIWORD(result)), (i - 1) * height);
597 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
600 /* Testing position at end of text */
601 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
602 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
603 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
605 /* Testing position way past end of text */
606 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
607 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
608 expected = (rtl ? 8 : 1);
609 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
611 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
612 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
613 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
615 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
616 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
617 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
618 xpos = LOWORD(result);
620 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
621 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
622 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
623 ok((signed short)(LOWORD(result)) < xpos,
624 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
625 (signed short)(LOWORD(result)), xpos);
626 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
628 /* Test around end of text that doesn't end in a newline. */
629 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
630 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
631 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
632 ok(pt.x > 1, "pt.x = %d\n", pt.x);
633 xpos = pt.x;
634 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
635 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
636 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
637 xpos = (rtl ? pt.x + 7 : pt.x);
638 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
639 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
640 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
642 /* Try a negative position. */
643 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
644 ok(pt.x == 1, "pt.x = %d\n", pt.x);
646 DestroyWindow(hwndRichEdit);
649 static void test_EM_SETCHARFORMAT(void)
651 HWND hwndRichEdit = new_richedit(NULL);
652 CHARFORMAT2 cf2;
653 int rc = 0;
654 int tested_effects[] = {
655 CFE_BOLD,
656 CFE_ITALIC,
657 CFE_UNDERLINE,
658 CFE_STRIKEOUT,
659 CFE_PROTECTED,
660 CFE_LINK,
661 CFE_SUBSCRIPT,
662 CFE_SUPERSCRIPT,
665 int i;
666 CHARRANGE cr;
667 LOCALESIGNATURE sig;
668 BOOL rtl;
670 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
671 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
672 (sig.lsUsb[3] & 0x08000000) != 0);
674 /* Invalid flags, CHARFORMAT2 structure blanked out */
675 memset(&cf2, 0, sizeof(cf2));
676 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
677 (LPARAM) &cf2);
678 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
680 /* A valid flag, CHARFORMAT2 structure blanked out */
681 memset(&cf2, 0, sizeof(cf2));
682 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
683 (LPARAM) &cf2);
684 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
686 /* A valid flag, CHARFORMAT2 structure blanked out */
687 memset(&cf2, 0, sizeof(cf2));
688 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
689 (LPARAM) &cf2);
690 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
692 /* A valid flag, CHARFORMAT2 structure blanked out */
693 memset(&cf2, 0, sizeof(cf2));
694 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
695 (LPARAM) &cf2);
696 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
698 /* A valid flag, CHARFORMAT2 structure blanked out */
699 memset(&cf2, 0, sizeof(cf2));
700 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
701 (LPARAM) &cf2);
702 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
704 /* Invalid flags, CHARFORMAT2 structure minimally filled */
705 memset(&cf2, 0, sizeof(cf2));
706 cf2.cbSize = sizeof(CHARFORMAT2);
707 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
708 (LPARAM) &cf2);
709 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
710 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
711 ok(rc == FALSE, "Should not be able to undo here.\n");
712 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
714 /* A valid flag, CHARFORMAT2 structure minimally filled */
715 memset(&cf2, 0, sizeof(cf2));
716 cf2.cbSize = sizeof(CHARFORMAT2);
717 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
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_SELECTION,
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_WORD,
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 todo_wine ok(rc == TRUE, "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_ALL,
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 ok(rc == TRUE, "Should not be able to undo here.\n");
752 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
754 cf2.cbSize = sizeof(CHARFORMAT2);
755 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
756 (LPARAM) &cf2);
758 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
759 cf2.cbSize = sizeof(CHARFORMAT2);
760 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
761 (LPARAM) &cf2);
762 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
763 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
765 /* wParam==0 is default char format, does not set modify */
766 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
767 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
768 ok(rc == 0, "Text marked as modified, expected not modified!\n");
769 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
770 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
771 if (! rtl)
773 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
774 ok(rc == 0, "Text marked as modified, expected not modified!\n");
776 else
777 skip("RTL language found\n");
779 /* wParam==SCF_SELECTION sets modify if nonempty selection */
780 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
781 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
782 ok(rc == 0, "Text marked as modified, expected not modified!\n");
783 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
784 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
785 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
786 ok(rc == 0, "Text marked as modified, expected not modified!\n");
788 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
789 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
790 ok(rc == 0, "Text marked as modified, expected not modified!\n");
791 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
792 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
793 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
794 ok(rc == 0, "Text marked as modified, expected not modified!\n");
795 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
796 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
797 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
798 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
799 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
801 /* wParam==SCF_ALL sets modify regardless of whether text is present */
802 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
803 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
804 ok(rc == 0, "Text marked as modified, expected not modified!\n");
805 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
806 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
807 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
808 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
810 DestroyWindow(hwndRichEdit);
812 /* EM_GETCHARFORMAT tests */
813 for (i = 0; tested_effects[i]; i++)
815 hwndRichEdit = new_richedit(NULL);
816 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
818 /* Need to set a TrueType font to get consistent CFM_BOLD results */
819 memset(&cf2, 0, sizeof(CHARFORMAT2));
820 cf2.cbSize = sizeof(CHARFORMAT2);
821 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
822 cf2.dwEffects = 0;
823 strcpy(cf2.szFaceName, "Courier New");
824 cf2.wWeight = FW_DONTCARE;
825 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
827 memset(&cf2, 0, sizeof(CHARFORMAT2));
828 cf2.cbSize = sizeof(CHARFORMAT2);
829 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
830 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
831 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
832 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
834 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
835 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
836 ok((cf2.dwEffects & tested_effects[i]) == 0,
837 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
839 memset(&cf2, 0, sizeof(CHARFORMAT2));
840 cf2.cbSize = sizeof(CHARFORMAT2);
841 cf2.dwMask = tested_effects[i];
842 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
843 cf2.dwMask = CFM_SUPERSCRIPT;
844 cf2.dwEffects = tested_effects[i];
845 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
846 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
848 memset(&cf2, 0, sizeof(CHARFORMAT2));
849 cf2.cbSize = sizeof(CHARFORMAT2);
850 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
851 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
852 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
853 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
855 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
856 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
857 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
858 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
860 memset(&cf2, 0, sizeof(CHARFORMAT2));
861 cf2.cbSize = sizeof(CHARFORMAT2);
862 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
863 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
864 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
865 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
867 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
868 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
869 ok((cf2.dwEffects & tested_effects[i]) == 0,
870 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
872 memset(&cf2, 0, sizeof(CHARFORMAT2));
873 cf2.cbSize = sizeof(CHARFORMAT2);
874 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
875 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
876 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
877 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
879 (cf2.dwMask & tested_effects[i]) == 0),
880 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
882 DestroyWindow(hwndRichEdit);
885 for (i = 0; tested_effects[i]; i++)
887 hwndRichEdit = new_richedit(NULL);
888 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
890 /* Need to set a TrueType font to get consistent CFM_BOLD results */
891 memset(&cf2, 0, sizeof(CHARFORMAT2));
892 cf2.cbSize = sizeof(CHARFORMAT2);
893 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
894 cf2.dwEffects = 0;
895 strcpy(cf2.szFaceName, "Courier New");
896 cf2.wWeight = FW_DONTCARE;
897 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
899 memset(&cf2, 0, sizeof(CHARFORMAT2));
900 cf2.cbSize = sizeof(CHARFORMAT2);
901 cf2.dwMask = tested_effects[i];
902 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
903 cf2.dwMask = CFM_SUPERSCRIPT;
904 cf2.dwEffects = tested_effects[i];
905 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
906 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
908 memset(&cf2, 0, sizeof(CHARFORMAT2));
909 cf2.cbSize = sizeof(CHARFORMAT2);
910 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
911 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
912 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
913 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
915 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
916 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
917 ok((cf2.dwEffects & tested_effects[i]) == 0,
918 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
920 memset(&cf2, 0, sizeof(CHARFORMAT2));
921 cf2.cbSize = sizeof(CHARFORMAT2);
922 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
923 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
924 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
925 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
927 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
928 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
929 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
930 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
932 memset(&cf2, 0, sizeof(CHARFORMAT2));
933 cf2.cbSize = sizeof(CHARFORMAT2);
934 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
935 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
936 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
937 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
939 (cf2.dwMask & tested_effects[i]) == 0),
940 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
941 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
942 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
944 DestroyWindow(hwndRichEdit);
947 /* Effects applied on an empty selection should take effect when selection is
948 replaced with text */
949 hwndRichEdit = new_richedit(NULL);
950 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
951 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
953 memset(&cf2, 0, sizeof(CHARFORMAT2));
954 cf2.cbSize = sizeof(CHARFORMAT2);
955 cf2.dwMask = CFM_BOLD;
956 cf2.dwEffects = CFE_BOLD;
957 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
959 /* Selection is now nonempty */
960 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
962 memset(&cf2, 0, sizeof(CHARFORMAT2));
963 cf2.cbSize = sizeof(CHARFORMAT2);
964 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
965 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
967 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
968 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
969 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
970 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
973 /* Set two effects on an empty selection */
974 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
975 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
977 memset(&cf2, 0, sizeof(CHARFORMAT2));
978 cf2.cbSize = sizeof(CHARFORMAT2);
979 cf2.dwMask = CFM_BOLD;
980 cf2.dwEffects = CFE_BOLD;
981 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
982 cf2.dwMask = CFM_ITALIC;
983 cf2.dwEffects = CFE_ITALIC;
984 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
986 /* Selection is now nonempty */
987 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
989 memset(&cf2, 0, sizeof(CHARFORMAT2));
990 cf2.cbSize = sizeof(CHARFORMAT2);
991 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
992 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
994 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
995 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
996 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
997 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
999 /* Setting the (empty) selection to exactly the same place as before should
1000 NOT clear the insertion style! */
1001 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1002 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1004 memset(&cf2, 0, sizeof(CHARFORMAT2));
1005 cf2.cbSize = sizeof(CHARFORMAT2);
1006 cf2.dwMask = CFM_BOLD;
1007 cf2.dwEffects = CFE_BOLD;
1008 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1010 /* Empty selection in same place, insert style should NOT be forgotten here. */
1011 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1013 /* Selection is now nonempty */
1014 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1016 memset(&cf2, 0, sizeof(CHARFORMAT2));
1017 cf2.cbSize = sizeof(CHARFORMAT2);
1018 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1019 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1021 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1022 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1023 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1024 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1026 /* Ditto with EM_EXSETSEL */
1027 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1028 cr.cpMin = 2; cr.cpMax = 2;
1029 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1031 memset(&cf2, 0, sizeof(CHARFORMAT2));
1032 cf2.cbSize = sizeof(CHARFORMAT2);
1033 cf2.dwMask = CFM_BOLD;
1034 cf2.dwEffects = CFE_BOLD;
1035 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1037 /* Empty selection in same place, insert style should NOT be forgotten here. */
1038 cr.cpMin = 2; cr.cpMax = 2;
1039 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1041 /* Selection is now nonempty */
1042 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1044 memset(&cf2, 0, sizeof(CHARFORMAT2));
1045 cf2.cbSize = sizeof(CHARFORMAT2);
1046 cr.cpMin = 2; cr.cpMax = 6;
1047 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1048 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1050 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1051 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1052 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1053 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1055 DestroyWindow(hwndRichEdit);
1058 static void test_EM_SETTEXTMODE(void)
1060 HWND hwndRichEdit = new_richedit(NULL);
1061 CHARFORMAT2 cf2, cf2test;
1062 CHARRANGE cr;
1063 int rc = 0;
1065 /*Attempt to use mutually exclusive modes*/
1066 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT|TM_RICHTEXT, 0);
1067 ok(rc == E_INVALIDARG,
1068 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1070 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1071 /*Insert text into the control*/
1073 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1075 /*Attempt to change the control to plain text mode*/
1076 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1077 ok(rc == E_UNEXPECTED,
1078 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1080 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1081 If rich text is pasted, it should have the same formatting as the rest
1082 of the text in the control*/
1084 /*Italicize the text
1085 *NOTE: If the default text was already italicized, the test will simply
1086 reverse; in other words, it will copy a regular "wine" into a plain
1087 text window that uses an italicized format*/
1088 cf2.cbSize = sizeof(CHARFORMAT2);
1089 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1090 (LPARAM) &cf2);
1092 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1093 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1095 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1096 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1098 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1099 however, SCF_ALL has been implemented*/
1100 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1101 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1103 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1104 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1108 /*Select the string "wine"*/
1109 cr.cpMin = 0;
1110 cr.cpMax = 4;
1111 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1113 /*Copy the italicized "wine" to the clipboard*/
1114 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1116 /*Reset the formatting to default*/
1117 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1118 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1119 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1121 /*Clear the text in the control*/
1122 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1124 /*Switch to Plain Text Mode*/
1125 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1126 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1128 /*Input "wine" again in normal format*/
1129 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1131 /*Paste the italicized "wine" into the control*/
1132 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1134 /*Select a character from the first "wine" string*/
1135 cr.cpMin = 2;
1136 cr.cpMax = 3;
1137 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1139 /*Retrieve its formatting*/
1140 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1141 (LPARAM) &cf2);
1143 /*Select a character from the second "wine" string*/
1144 cr.cpMin = 5;
1145 cr.cpMax = 6;
1146 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1148 /*Retrieve its formatting*/
1149 cf2test.cbSize = sizeof(CHARFORMAT2);
1150 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1151 (LPARAM) &cf2test);
1153 /*Compare the two formattings*/
1154 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1155 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1156 cf2.dwEffects, cf2test.dwEffects);
1157 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1158 printing "wine" in the current format(normal)
1159 pasting "wine" from the clipboard(italicized)
1160 comparing the two formats(should differ)*/
1162 /*Attempt to switch with text in control*/
1163 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1164 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1166 /*Clear control*/
1167 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1169 /*Switch into Rich Text mode*/
1170 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1171 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1173 /*Print "wine" in normal formatting into the control*/
1174 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1176 /*Paste italicized "wine" into the control*/
1177 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1179 /*Select text from the first "wine" string*/
1180 cr.cpMin = 1;
1181 cr.cpMax = 3;
1182 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1184 /*Retrieve its formatting*/
1185 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1186 (LPARAM) &cf2);
1188 /*Select text from the second "wine" string*/
1189 cr.cpMin = 6;
1190 cr.cpMax = 7;
1191 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1193 /*Retrieve its formatting*/
1194 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1195 (LPARAM) &cf2test);
1197 /*Test that the two formattings are not the same*/
1198 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1199 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1200 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1202 DestroyWindow(hwndRichEdit);
1205 static void test_SETPARAFORMAT(void)
1207 HWND hwndRichEdit = new_richedit(NULL);
1208 PARAFORMAT2 fmt;
1209 HRESULT ret;
1210 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1211 fmt.cbSize = sizeof(PARAFORMAT2);
1212 fmt.dwMask = PFM_ALIGNMENT;
1213 fmt.wAlignment = PFA_LEFT;
1215 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1216 ok(ret != 0, "expected non-zero got %d\n", ret);
1218 fmt.cbSize = sizeof(PARAFORMAT2);
1219 fmt.dwMask = -1;
1220 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1221 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1222 * between richedit different native builds of riched20.dll
1223 * used on different Windows versions. */
1224 ret &= ~PFM_TABLEROWDELIMITER;
1225 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1227 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1228 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1230 DestroyWindow(hwndRichEdit);
1233 static void test_TM_PLAINTEXT(void)
1235 /*Tests plain text properties*/
1237 HWND hwndRichEdit = new_richedit(NULL);
1238 CHARFORMAT2 cf2, cf2test;
1239 CHARRANGE cr;
1240 int rc = 0;
1242 /*Switch to plain text mode*/
1244 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1245 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1247 /*Fill control with text*/
1249 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1251 /*Select some text and bold it*/
1253 cr.cpMin = 10;
1254 cr.cpMax = 20;
1255 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1256 cf2.cbSize = sizeof(CHARFORMAT2);
1257 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1259 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1260 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1262 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1263 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1265 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1266 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1268 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1269 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1271 /*Get the formatting of those characters*/
1273 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1275 /*Get the formatting of some other characters*/
1276 cf2test.cbSize = sizeof(CHARFORMAT2);
1277 cr.cpMin = 21;
1278 cr.cpMax = 30;
1279 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1280 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1282 /*Test that they are the same as plain text allows only one formatting*/
1284 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1285 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1286 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1288 /*Fill the control with a "wine" string, which when inserted will be bold*/
1290 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1292 /*Copy the bolded "wine" string*/
1294 cr.cpMin = 0;
1295 cr.cpMax = 4;
1296 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1297 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1299 /*Swap back to rich text*/
1301 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1302 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1304 /*Set the default formatting to bold italics*/
1306 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1307 cf2.dwMask |= CFM_ITALIC;
1308 cf2.dwEffects ^= CFE_ITALIC;
1309 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1310 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1312 /*Set the text in the control to "wine", which will be bold and italicized*/
1314 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1316 /*Paste the plain text "wine" string, which should take the insert
1317 formatting, which at the moment is bold italics*/
1319 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1321 /*Select the first "wine" string and retrieve its formatting*/
1323 cr.cpMin = 1;
1324 cr.cpMax = 3;
1325 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1326 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1328 /*Select the second "wine" string and retrieve its formatting*/
1330 cr.cpMin = 5;
1331 cr.cpMax = 7;
1332 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1333 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1335 /*Compare the two formattings. They should be the same.*/
1337 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1338 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1339 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1340 DestroyWindow(hwndRichEdit);
1343 static void test_WM_GETTEXT(void)
1345 HWND hwndRichEdit = new_richedit(NULL);
1346 static const char text[] = "Hello. My name is RichEdit!";
1347 static const char text2[] = "Hello. My name is RichEdit!\r";
1348 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1349 char buffer[1024] = {0};
1350 int result;
1352 /* Baseline test with normal-sized buffer */
1353 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1354 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1355 ok(result == lstrlen(buffer),
1356 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1357 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1358 result = strcmp(buffer,text);
1359 ok(result == 0,
1360 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1362 /* Test for returned value of WM_GETTEXTLENGTH */
1363 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1364 ok(result == lstrlen(text),
1365 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1366 result, lstrlen(text));
1368 /* Test for behavior in overflow case */
1369 memset(buffer, 0, 1024);
1370 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1371 ok(result == 0 ||
1372 result == lstrlenA(text) - 1, /* XP, win2k3 */
1373 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1374 result = strcmp(buffer,text);
1375 if (result)
1376 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1377 ok(result == 0,
1378 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1380 /* Baseline test with normal-sized buffer and carriage return */
1381 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1382 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1383 ok(result == lstrlen(buffer),
1384 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1385 result = strcmp(buffer,text2_after);
1386 ok(result == 0,
1387 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1389 /* Test for returned value of WM_GETTEXTLENGTH */
1390 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1391 ok(result == lstrlen(text2_after),
1392 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1393 result, lstrlen(text2_after));
1395 /* Test for behavior of CRLF conversion in case of overflow */
1396 memset(buffer, 0, 1024);
1397 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1398 ok(result == 0 ||
1399 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1400 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1401 result = strcmp(buffer,text2);
1402 if (result)
1403 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1404 ok(result == 0,
1405 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1407 DestroyWindow(hwndRichEdit);
1410 static void test_EM_GETTEXTRANGE(void)
1412 HWND hwndRichEdit = new_richedit(NULL);
1413 const char * text1 = "foo bar\r\nfoo bar";
1414 const char * text2 = "foo bar\rfoo bar";
1415 const char * expect = "bar\rfoo";
1416 char buffer[1024] = {0};
1417 LRESULT result;
1418 TEXTRANGEA textRange;
1420 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1422 textRange.lpstrText = buffer;
1423 textRange.chrg.cpMin = 4;
1424 textRange.chrg.cpMax = 11;
1425 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1426 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1427 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1431 textRange.lpstrText = buffer;
1432 textRange.chrg.cpMin = 4;
1433 textRange.chrg.cpMax = 11;
1434 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1435 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1436 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1438 /* cpMax of text length is used instead of -1 in this case */
1439 textRange.lpstrText = buffer;
1440 textRange.chrg.cpMin = 0;
1441 textRange.chrg.cpMax = -1;
1442 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1443 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1444 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1446 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1447 textRange.lpstrText = buffer;
1448 textRange.chrg.cpMin = -1;
1449 textRange.chrg.cpMax = 1;
1450 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1451 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1452 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1454 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1455 textRange.lpstrText = buffer;
1456 textRange.chrg.cpMin = 1;
1457 textRange.chrg.cpMax = -1;
1458 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1459 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1460 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1462 /* no end character is copied if cpMax - cpMin < 0 */
1463 textRange.lpstrText = buffer;
1464 textRange.chrg.cpMin = 5;
1465 textRange.chrg.cpMax = 5;
1466 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1467 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1468 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1470 /* cpMax of text length is used if cpMax > text length*/
1471 textRange.lpstrText = buffer;
1472 textRange.chrg.cpMin = 0;
1473 textRange.chrg.cpMax = 1000;
1474 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1475 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1476 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1478 DestroyWindow(hwndRichEdit);
1481 static void test_EM_GETSELTEXT(void)
1483 HWND hwndRichEdit = new_richedit(NULL);
1484 const char * text1 = "foo bar\r\nfoo bar";
1485 const char * text2 = "foo bar\rfoo bar";
1486 const char * expect = "bar\rfoo";
1487 char buffer[1024] = {0};
1488 LRESULT result;
1490 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1492 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1493 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1494 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1495 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1497 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1499 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1500 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1501 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1502 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1504 DestroyWindow(hwndRichEdit);
1507 /* FIXME: need to test unimplemented options and robustly test wparam */
1508 static void test_EM_SETOPTIONS(void)
1510 HWND hwndRichEdit;
1511 static const char text[] = "Hello. My name is RichEdit!";
1512 char buffer[1024] = {0};
1513 DWORD dwStyle, options, oldOptions;
1514 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1515 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1516 ES_SELECTIONBAR|ES_VERTICAL;
1518 /* Test initial options. */
1519 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1520 0, 0, 200, 60, NULL, NULL,
1521 hmoduleRichEdit, NULL);
1522 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1523 RICHEDIT_CLASS, (int) GetLastError());
1524 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1525 ok(options == 0, "Incorrect initial options %x\n", options);
1526 DestroyWindow(hwndRichEdit);
1528 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1529 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
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 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1536 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1537 "Incorrect initial options %x\n", options);
1539 /* NEGATIVE TESTING - NO OPTIONS SET */
1540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1541 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1543 /* testing no readonly by sending 'a' to the control*/
1544 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1545 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1546 ok(buffer[0]=='a',
1547 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1548 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1550 /* READONLY - sending 'a' to the control */
1551 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1552 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1553 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1554 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1555 ok(buffer[0]==text[0],
1556 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1558 /* EM_SETOPTIONS changes the window style, but changing the
1559 * window style does not change the options. */
1560 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1561 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1562 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1563 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1564 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1565 /* Confirm that the text is still read only. */
1566 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1567 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1568 ok(buffer[0]==text[0],
1569 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1571 oldOptions = options;
1572 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1573 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1574 ok(options == oldOptions,
1575 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1577 DestroyWindow(hwndRichEdit);
1580 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1582 CHARFORMAT2W text_format;
1583 text_format.cbSize = sizeof(text_format);
1584 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1585 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1586 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1589 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1591 int link_present = 0;
1593 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1594 if (is_url)
1595 { /* control text is url; should get CFE_LINK */
1596 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1598 else
1600 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1604 static HWND new_static_wnd(HWND parent) {
1605 return new_window("Static", 0, parent);
1608 static void test_EM_AUTOURLDETECT(void)
1610 /* DO NOT change the properties of the first two elements. To shorten the
1611 tests, all tests after WM_SETTEXT test just the first two elements -
1612 one non-URL and one URL */
1613 struct urls_s {
1614 const char *text;
1615 int is_url;
1616 } urls[12] = {
1617 {"winehq.org", 0},
1618 {"http://www.winehq.org", 1},
1619 {"http//winehq.org", 0},
1620 {"ww.winehq.org", 0},
1621 {"www.winehq.org", 1},
1622 {"ftp://192.168.1.1", 1},
1623 {"ftp//192.168.1.1", 0},
1624 {"mailto:your@email.com", 1},
1625 {"prospero:prosperoserver", 1},
1626 {"telnet:test", 1},
1627 {"news:newserver", 1},
1628 {"wais:waisserver", 1}
1631 int i, j;
1632 int urlRet=-1;
1633 HWND hwndRichEdit, parent;
1635 /* All of the following should cause the URL to be detected */
1636 const char * templates_delim[] = {
1637 "This is some text with X on it",
1638 "This is some text with (X) on it",
1639 "This is some text with X\r on it",
1640 "This is some text with ---X--- on it",
1641 "This is some text with \"X\" on it",
1642 "This is some text with 'X' on it",
1643 "This is some text with 'X' on it",
1644 "This is some text with :X: on it",
1646 "This text ends with X",
1648 "This is some text with X) on it",
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' on it",
1652 "This is some text with X: on it",
1654 "This is some text with (X on it",
1655 "This is some text with \rX on it",
1656 "This is some text with ---X on it",
1657 "This is some text with \"X on it",
1658 "This is some text with 'X on it",
1659 "This is some text with :X on it",
1661 /* None of these should cause the URL to be detected */
1662 const char * templates_non_delim[] = {
1663 "This is some text with |X| on it",
1664 "This is some text with *X* on it",
1665 "This is some text with /X/ on it",
1666 "This is some text with +X+ on it",
1667 "This is some text with %X% 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",
1672 "This is some text with *X on it",
1673 "This is some text with /X on it",
1674 "This is some text with +X on it",
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",
1680 /* All of these cause the URL detection to be extended by one more byte,
1681 thus demonstrating that the tested character is considered as part
1682 of the URL. */
1683 const char * templates_xten_delim[] = {
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",
1691 "This is some text with X\\ on it",
1693 char buffer[1024];
1695 parent = new_static_wnd(NULL);
1696 hwndRichEdit = new_richedit(parent);
1697 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1698 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1699 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1700 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1701 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1702 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1703 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1704 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1705 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1706 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1707 /* for each url, check the text to see if CFE_LINK effect is present */
1708 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1710 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1711 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1712 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1714 /* Link detection should happen immediately upon WM_SETTEXT */
1715 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1716 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1717 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1719 DestroyWindow(hwndRichEdit);
1721 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1722 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1723 hwndRichEdit = new_richedit(parent);
1725 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1726 char * at_pos;
1727 int at_offset;
1728 int end_offset;
1730 at_pos = strchr(templates_delim[j], 'X');
1731 at_offset = at_pos - templates_delim[j];
1732 strncpy(buffer, templates_delim[j], at_offset);
1733 buffer[at_offset] = '\0';
1734 strcat(buffer, urls[i].text);
1735 strcat(buffer, templates_delim[j] + at_offset + 1);
1736 end_offset = at_offset + strlen(urls[i].text);
1738 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1741 /* This assumes no templates start with the URL itself, and that they
1742 have at least two characters before the URL text */
1743 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1744 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1745 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1746 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1747 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1748 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1750 if (urls[i].is_url)
1752 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1753 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1754 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1755 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1757 else
1759 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1760 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1761 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1762 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1764 if (buffer[end_offset] != '\0')
1766 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1767 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1768 if (buffer[end_offset +1] != '\0')
1770 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1771 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1776 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1777 char * at_pos;
1778 int at_offset;
1779 int end_offset;
1781 at_pos = strchr(templates_non_delim[j], 'X');
1782 at_offset = at_pos - templates_non_delim[j];
1783 strncpy(buffer, templates_non_delim[j], at_offset);
1784 buffer[at_offset] = '\0';
1785 strcat(buffer, urls[i].text);
1786 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1787 end_offset = at_offset + strlen(urls[i].text);
1789 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1790 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1792 /* This assumes no templates start with the URL itself, and that they
1793 have at least two characters before the URL text */
1794 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1795 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1796 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1797 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1798 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1799 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1801 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1802 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1803 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1804 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1805 if (buffer[end_offset] != '\0')
1807 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1808 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1809 if (buffer[end_offset +1] != '\0')
1811 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1812 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1817 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1818 char * at_pos;
1819 int at_offset;
1820 int end_offset;
1822 at_pos = strchr(templates_xten_delim[j], 'X');
1823 at_offset = at_pos - templates_xten_delim[j];
1824 strncpy(buffer, templates_xten_delim[j], at_offset);
1825 buffer[at_offset] = '\0';
1826 strcat(buffer, urls[i].text);
1827 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1828 end_offset = at_offset + strlen(urls[i].text);
1830 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1831 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1833 /* This assumes no templates start with the URL itself, and that they
1834 have at least two characters before the URL text */
1835 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1836 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1837 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1838 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1839 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1840 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1842 if (urls[i].is_url)
1844 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1845 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1846 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1847 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1848 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1849 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1851 else
1853 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1854 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1855 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1856 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1857 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1858 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1860 if (buffer[end_offset +1] != '\0')
1862 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1863 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1864 if (buffer[end_offset +2] != '\0')
1866 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1867 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1872 DestroyWindow(hwndRichEdit);
1873 hwndRichEdit = NULL;
1876 /* Test detection of URLs within normal text - WM_CHAR case. */
1877 /* Test only the first two URL examples for brevity */
1878 for (i = 0; i < 2; i++) {
1879 hwndRichEdit = new_richedit(parent);
1881 /* Also for brevity, test only the first three delimiters */
1882 for (j = 0; j < 3; j++) {
1883 char * at_pos;
1884 int at_offset;
1885 int end_offset;
1886 int u, v;
1888 at_pos = strchr(templates_delim[j], 'X');
1889 at_offset = at_pos - templates_delim[j];
1890 end_offset = at_offset + strlen(urls[i].text);
1892 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1893 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1894 for (u = 0; templates_delim[j][u]; u++) {
1895 if (templates_delim[j][u] == '\r') {
1896 simulate_typing_characters(hwndRichEdit, "\r");
1897 } else if (templates_delim[j][u] != 'X') {
1898 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1899 } else {
1900 for (v = 0; urls[i].text[v]; v++) {
1901 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1905 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1907 /* This assumes no templates start with the URL itself, and that they
1908 have at least two characters before the URL text */
1909 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1910 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1911 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1912 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1913 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1914 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1916 if (urls[i].is_url)
1918 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1919 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1920 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1921 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1923 else
1925 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1926 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1927 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1928 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1930 if (buffer[end_offset] != '\0')
1932 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1933 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1934 if (buffer[end_offset +1] != '\0')
1936 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1937 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1941 /* The following will insert a paragraph break after the first character
1942 of the URL candidate, thus breaking the URL. It is expected that the
1943 CFE_LINK attribute should break across both pieces of the URL */
1944 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1945 simulate_typing_characters(hwndRichEdit, "\r");
1946 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1948 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1949 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1950 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1951 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1952 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1953 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1955 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1956 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1957 /* end_offset moved because of paragraph break */
1958 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1959 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1960 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1961 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1963 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1964 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1965 if (buffer[end_offset +2] != '\0')
1967 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1968 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1972 /* The following will remove the just-inserted paragraph break, thus
1973 restoring the URL */
1974 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1975 simulate_typing_characters(hwndRichEdit, "\b");
1976 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1978 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1979 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1980 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1981 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1982 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1983 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1985 if (urls[i].is_url)
1987 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1988 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1989 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1990 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1992 else
1994 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1995 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1996 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1997 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1999 if (buffer[end_offset] != '\0')
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2003 if (buffer[end_offset +1] != '\0')
2005 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2006 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2010 DestroyWindow(hwndRichEdit);
2011 hwndRichEdit = NULL;
2014 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2015 /* Test just the first two URL examples for brevity */
2016 for (i = 0; i < 2; i++) {
2017 SETTEXTEX st;
2019 hwndRichEdit = new_richedit(parent);
2021 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2022 be detected:
2023 1) Set entire text, a la WM_SETTEXT
2024 2) Set a selection of the text to the URL
2025 3) Set a portion of the text at a time, which eventually results in
2026 an URL
2027 All of them should give equivalent results
2030 /* Set entire text in one go, like WM_SETTEXT */
2031 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2032 char * at_pos;
2033 int at_offset;
2034 int end_offset;
2036 st.codepage = CP_ACP;
2037 st.flags = ST_DEFAULT;
2039 at_pos = strchr(templates_delim[j], 'X');
2040 at_offset = at_pos - templates_delim[j];
2041 strncpy(buffer, templates_delim[j], at_offset);
2042 buffer[at_offset] = '\0';
2043 strcat(buffer, urls[i].text);
2044 strcat(buffer, templates_delim[j] + at_offset + 1);
2045 end_offset = at_offset + strlen(urls[i].text);
2047 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2048 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2050 /* This assumes no templates start with the URL itself, and that they
2051 have at least two characters before the URL text */
2052 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2053 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2054 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2055 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2056 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2057 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2059 if (urls[i].is_url)
2061 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2062 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2063 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2064 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2066 else
2068 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2069 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2070 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2071 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2073 if (buffer[end_offset] != '\0')
2075 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2076 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2077 if (buffer[end_offset +1] != '\0')
2079 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2080 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2085 /* Set selection with X to the URL */
2086 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2087 char * at_pos;
2088 int at_offset;
2089 int end_offset;
2091 at_pos = strchr(templates_delim[j], 'X');
2092 at_offset = at_pos - templates_delim[j];
2093 end_offset = at_offset + strlen(urls[i].text);
2095 st.codepage = CP_ACP;
2096 st.flags = ST_DEFAULT;
2097 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2098 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2099 st.flags = ST_SELECTION;
2100 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2101 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2102 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2104 /* This assumes no templates start with the URL itself, and that they
2105 have at least two characters before the URL text */
2106 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2107 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2108 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2109 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2110 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2111 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2113 if (urls[i].is_url)
2115 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2116 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2117 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2118 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2120 else
2122 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2123 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2124 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2125 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2127 if (buffer[end_offset] != '\0')
2129 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2130 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2131 if (buffer[end_offset +1] != '\0')
2133 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2134 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2139 /* Set selection with X to the first character of the URL, then the rest */
2140 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2141 char * at_pos;
2142 int at_offset;
2143 int end_offset;
2145 at_pos = strchr(templates_delim[j], 'X');
2146 at_offset = at_pos - templates_delim[j];
2147 end_offset = at_offset + strlen(urls[i].text);
2149 strcpy(buffer, "YY");
2150 buffer[0] = urls[i].text[0];
2152 st.codepage = CP_ACP;
2153 st.flags = ST_DEFAULT;
2154 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2155 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2156 st.flags = ST_SELECTION;
2157 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2158 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2159 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2160 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2161 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2163 /* This assumes no templates start with the URL itself, and that they
2164 have at least two characters before the URL text */
2165 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2166 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2167 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2168 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2169 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2170 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2172 if (urls[i].is_url)
2174 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2175 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2176 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2177 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2179 else
2181 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2182 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2183 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2184 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2186 if (buffer[end_offset] != '\0')
2188 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2189 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2190 if (buffer[end_offset +1] != '\0')
2192 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2193 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2198 DestroyWindow(hwndRichEdit);
2199 hwndRichEdit = NULL;
2202 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2203 /* Test just the first two URL examples for brevity */
2204 for (i = 0; i < 2; i++) {
2205 hwndRichEdit = new_richedit(parent);
2207 /* Set selection with X to the URL */
2208 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2209 char * at_pos;
2210 int at_offset;
2211 int end_offset;
2213 at_pos = strchr(templates_delim[j], 'X');
2214 at_offset = at_pos - templates_delim[j];
2215 end_offset = at_offset + strlen(urls[i].text);
2217 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2218 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2219 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2220 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2221 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2223 /* This assumes no templates start with the URL itself, and that they
2224 have at least two characters before the URL text */
2225 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2226 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2227 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2228 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2229 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2230 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2232 if (urls[i].is_url)
2234 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2235 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2236 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2237 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2239 else
2241 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2242 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2243 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2244 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2246 if (buffer[end_offset] != '\0')
2248 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2249 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2250 if (buffer[end_offset +1] != '\0')
2252 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2253 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2258 /* Set selection with X to the first character of the URL, then the rest */
2259 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2260 char * at_pos;
2261 int at_offset;
2262 int end_offset;
2264 at_pos = strchr(templates_delim[j], 'X');
2265 at_offset = at_pos - templates_delim[j];
2266 end_offset = at_offset + strlen(urls[i].text);
2268 strcpy(buffer, "YY");
2269 buffer[0] = urls[i].text[0];
2271 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2272 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2273 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2274 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2275 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2276 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2277 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2279 /* This assumes no templates start with the URL itself, and that they
2280 have at least two characters before the URL text */
2281 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2282 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2283 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2284 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2285 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2286 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2288 if (urls[i].is_url)
2290 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2291 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2292 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2293 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2295 else
2297 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2298 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2299 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2300 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2302 if (buffer[end_offset] != '\0')
2304 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2305 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2306 if (buffer[end_offset +1] != '\0')
2308 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2309 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2314 DestroyWindow(hwndRichEdit);
2315 hwndRichEdit = NULL;
2318 DestroyWindow(parent);
2321 static void test_EM_SCROLL(void)
2323 int i, j;
2324 int r; /* return value */
2325 int expr; /* expected return value */
2326 HWND hwndRichEdit = new_richedit(NULL);
2327 int y_before, y_after; /* units of lines of text */
2329 /* test a richedit box containing a single line of text */
2330 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2331 expr = 0x00010000;
2332 for (i = 0; i < 4; i++) {
2333 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2335 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2336 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2337 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2338 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2339 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2340 "(i == %d)\n", y_after, i);
2344 * test a richedit box that will scroll. There are two general
2345 * cases: the case without any long lines and the case with a long
2346 * line.
2348 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2349 if (i == 0)
2350 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2351 else
2352 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2353 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2354 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2355 "LONG LINE \nb\nc\nd\ne");
2356 for (j = 0; j < 12; j++) /* reset scroll position to top */
2357 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2359 /* get first visible line */
2360 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2361 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2363 /* get new current first visible line */
2364 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2366 ok(((r & 0xffffff00) == 0x00010000) &&
2367 ((r & 0x000000ff) != 0x00000000),
2368 "EM_SCROLL page down didn't scroll by a small positive number of "
2369 "lines (r == 0x%08x)\n", r);
2370 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2371 "(line %d scrolled to line %d\n", y_before, y_after);
2373 y_before = y_after;
2375 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2376 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2377 ok(((r & 0xffffff00) == 0x0001ff00),
2378 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2379 "(r == 0x%08x)\n", r);
2380 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2381 "%d scrolled to line %d\n", y_before, y_after);
2383 y_before = y_after;
2385 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2387 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2389 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2390 "(r == 0x%08x)\n", r);
2391 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2392 "1 line (%d scrolled to %d)\n", y_before, y_after);
2394 y_before = y_after;
2396 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2398 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2400 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2401 "(r == 0x%08x)\n", r);
2402 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2403 "line (%d scrolled to %d)\n", y_before, y_after);
2405 y_before = y_after;
2407 r = SendMessage(hwndRichEdit, EM_SCROLL,
2408 SB_LINEUP, 0); /* lineup beyond top */
2410 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2412 ok(r == 0x00010000,
2413 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2414 ok(y_before == y_after,
2415 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2417 y_before = y_after;
2419 r = SendMessage(hwndRichEdit, EM_SCROLL,
2420 SB_PAGEUP, 0);/*page up beyond top */
2422 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2424 ok(r == 0x00010000,
2425 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2426 ok(y_before == y_after,
2427 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2429 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2430 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2431 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2432 r = SendMessage(hwndRichEdit, EM_SCROLL,
2433 SB_PAGEDOWN, 0); /* page down beyond bot */
2434 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2436 ok(r == 0x00010000,
2437 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2438 ok(y_before == y_after,
2439 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2440 y_before, y_after);
2442 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2443 SendMessage(hwndRichEdit, EM_SCROLL,
2444 SB_LINEDOWN, 0); /* line down beyond bot */
2445 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2447 ok(r == 0x00010000,
2448 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2449 ok(y_before == y_after,
2450 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2451 y_before, y_after);
2453 DestroyWindow(hwndRichEdit);
2456 static unsigned int recursionLevel = 0;
2457 static unsigned int WM_SIZE_recursionLevel = 0;
2458 static BOOL bailedOutOfRecursion = FALSE;
2459 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2461 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2463 LRESULT r;
2465 if (bailedOutOfRecursion) return 0;
2466 if (recursionLevel >= 32) {
2467 bailedOutOfRecursion = TRUE;
2468 return 0;
2471 recursionLevel++;
2472 switch (message) {
2473 case WM_SIZE:
2474 WM_SIZE_recursionLevel++;
2475 r = richeditProc(hwnd, message, wParam, lParam);
2476 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2477 ShowScrollBar(hwnd, SB_VERT, TRUE);
2478 WM_SIZE_recursionLevel--;
2479 break;
2480 default:
2481 r = richeditProc(hwnd, message, wParam, lParam);
2482 break;
2484 recursionLevel--;
2485 return r;
2488 static void test_scrollbar_visibility(void)
2490 HWND hwndRichEdit;
2491 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2492 SCROLLINFO si;
2493 WNDCLASSA cls;
2494 BOOL r;
2496 /* These tests show that richedit should temporarily refrain from automatically
2497 hiding or showing its scrollbars (vertical at least) when an explicit request
2498 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2499 Some applications depend on forced showing (when otherwise richedit would
2500 hide the vertical scrollbar) and are thrown on an endless recursive loop
2501 if richedit auto-hides the scrollbar again. Apparently they never heard of
2502 the ES_DISABLENOSCROLL style... */
2504 hwndRichEdit = new_richedit(NULL);
2506 /* Test default scrollbar visibility behavior */
2507 memset(&si, 0, sizeof(si));
2508 si.cbSize = sizeof(si);
2509 si.fMask = SIF_PAGE | SIF_RANGE;
2510 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2511 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2512 "Vertical scrollbar is visible, should be invisible.\n");
2513 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2514 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2515 si.nPage, si.nMin, si.nMax);
2517 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2518 memset(&si, 0, sizeof(si));
2519 si.cbSize = sizeof(si);
2520 si.fMask = SIF_PAGE | SIF_RANGE;
2521 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2522 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2523 "Vertical scrollbar is visible, should be invisible.\n");
2524 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2525 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2526 si.nPage, si.nMin, si.nMax);
2528 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2529 memset(&si, 0, sizeof(si));
2530 si.cbSize = sizeof(si);
2531 si.fMask = SIF_PAGE | SIF_RANGE;
2532 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2533 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2534 "Vertical scrollbar is invisible, should be visible.\n");
2535 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2536 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2537 si.nPage, si.nMin, si.nMax);
2539 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2540 even though it hides the scrollbar */
2541 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2542 memset(&si, 0, sizeof(si));
2543 si.cbSize = sizeof(si);
2544 si.fMask = SIF_PAGE | SIF_RANGE;
2545 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2546 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2547 "Vertical scrollbar is visible, should be invisible.\n");
2548 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2549 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2550 si.nPage, si.nMin, si.nMax);
2552 /* Setting non-scrolling text again does *not* reset scrollbar range */
2553 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2565 memset(&si, 0, sizeof(si));
2566 si.cbSize = sizeof(si);
2567 si.fMask = SIF_PAGE | SIF_RANGE;
2568 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2569 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2570 "Vertical scrollbar is visible, should be invisible.\n");
2571 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2572 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2573 si.nPage, si.nMin, si.nMax);
2575 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2576 memset(&si, 0, sizeof(si));
2577 si.cbSize = sizeof(si);
2578 si.fMask = SIF_PAGE | SIF_RANGE;
2579 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2580 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2581 "Vertical scrollbar is visible, should be invisible.\n");
2582 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2583 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2584 si.nPage, si.nMin, si.nMax);
2586 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2587 memset(&si, 0, sizeof(si));
2588 si.cbSize = sizeof(si);
2589 si.fMask = SIF_PAGE | SIF_RANGE;
2590 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2591 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2592 "Vertical scrollbar is visible, should be invisible.\n");
2593 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2594 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2595 si.nPage, si.nMin, si.nMax);
2597 DestroyWindow(hwndRichEdit);
2599 /* Test again, with ES_DISABLENOSCROLL style */
2600 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2602 /* Test default scrollbar visibility behavior */
2603 memset(&si, 0, sizeof(si));
2604 si.cbSize = sizeof(si);
2605 si.fMask = SIF_PAGE | SIF_RANGE;
2606 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2607 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2608 "Vertical scrollbar is invisible, should be visible.\n");
2609 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2610 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2611 si.nPage, si.nMin, si.nMax);
2613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2614 memset(&si, 0, sizeof(si));
2615 si.cbSize = sizeof(si);
2616 si.fMask = SIF_PAGE | SIF_RANGE;
2617 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2618 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2619 "Vertical scrollbar is invisible, should be visible.\n");
2620 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2621 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2622 si.nPage, si.nMin, si.nMax);
2624 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2625 memset(&si, 0, sizeof(si));
2626 si.cbSize = sizeof(si);
2627 si.fMask = SIF_PAGE | SIF_RANGE;
2628 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2629 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2630 "Vertical scrollbar is invisible, should be visible.\n");
2631 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2632 "reported page/range is %d (%d..%d)\n",
2633 si.nPage, si.nMin, si.nMax);
2635 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2636 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
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) expected nMax/nPage nonzero\n",
2645 si.nPage, si.nMin, si.nMax);
2647 /* Setting non-scrolling text again does *not* reset scrollbar range */
2648 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2660 memset(&si, 0, sizeof(si));
2661 si.cbSize = sizeof(si);
2662 si.fMask = SIF_PAGE | SIF_RANGE;
2663 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2664 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2665 "Vertical scrollbar is invisible, should be visible.\n");
2666 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2667 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2668 si.nPage, si.nMin, si.nMax);
2670 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2671 memset(&si, 0, sizeof(si));
2672 si.cbSize = sizeof(si);
2673 si.fMask = SIF_PAGE | SIF_RANGE;
2674 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2675 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2676 "Vertical scrollbar is invisible, should be visible.\n");
2677 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2678 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2679 si.nPage, si.nMin, si.nMax);
2681 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2682 memset(&si, 0, sizeof(si));
2683 si.cbSize = sizeof(si);
2684 si.fMask = SIF_PAGE | SIF_RANGE;
2685 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2686 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2687 "Vertical scrollbar is invisible, should be visible.\n");
2688 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2689 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2690 si.nPage, si.nMin, si.nMax);
2692 DestroyWindow(hwndRichEdit);
2694 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2695 hwndRichEdit = new_richedit(NULL);
2697 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2698 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2699 memset(&si, 0, sizeof(si));
2700 si.cbSize = sizeof(si);
2701 si.fMask = SIF_PAGE | SIF_RANGE;
2702 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2703 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2704 "Vertical scrollbar is invisible, should be visible.\n");
2705 todo_wine {
2706 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2707 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2708 si.nPage, si.nMin, si.nMax);
2711 /* Ditto, see above */
2712 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2713 memset(&si, 0, sizeof(si));
2714 si.cbSize = sizeof(si);
2715 si.fMask = SIF_PAGE | SIF_RANGE;
2716 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2717 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2718 "Vertical scrollbar is invisible, should be visible.\n");
2719 todo_wine {
2720 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2721 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2722 si.nPage, si.nMin, si.nMax);
2725 /* Ditto, see above */
2726 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2727 memset(&si, 0, sizeof(si));
2728 si.cbSize = sizeof(si);
2729 si.fMask = SIF_PAGE | SIF_RANGE;
2730 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2731 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2732 "Vertical scrollbar is invisible, should be visible.\n");
2733 todo_wine {
2734 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2735 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2736 si.nPage, si.nMin, si.nMax);
2739 /* Ditto, see above */
2740 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2741 memset(&si, 0, sizeof(si));
2742 si.cbSize = sizeof(si);
2743 si.fMask = SIF_PAGE | SIF_RANGE;
2744 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2745 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2746 "Vertical scrollbar is invisible, should be visible.\n");
2747 todo_wine {
2748 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2749 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2750 si.nPage, si.nMin, si.nMax);
2753 /* Ditto, see above */
2754 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2755 memset(&si, 0, sizeof(si));
2756 si.cbSize = sizeof(si);
2757 si.fMask = SIF_PAGE | SIF_RANGE;
2758 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2759 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2760 "Vertical scrollbar is invisible, should be visible.\n");
2761 todo_wine {
2762 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2763 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2764 si.nPage, si.nMin, si.nMax);
2767 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2768 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2769 memset(&si, 0, sizeof(si));
2770 si.cbSize = sizeof(si);
2771 si.fMask = SIF_PAGE | SIF_RANGE;
2772 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2773 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2774 "Vertical scrollbar is visible, should be invisible.\n");
2775 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2776 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2777 si.nPage, si.nMin, si.nMax);
2779 DestroyWindow(hwndRichEdit);
2781 hwndRichEdit = new_richedit(NULL);
2783 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2784 memset(&si, 0, sizeof(si));
2785 si.cbSize = sizeof(si);
2786 si.fMask = SIF_PAGE | SIF_RANGE;
2787 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2788 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2789 "Vertical scrollbar is visible, should be invisible.\n");
2790 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2791 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2792 si.nPage, si.nMin, si.nMax);
2794 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2795 memset(&si, 0, sizeof(si));
2796 si.cbSize = sizeof(si);
2797 si.fMask = SIF_PAGE | SIF_RANGE;
2798 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2799 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2800 "Vertical scrollbar is visible, should be invisible.\n");
2801 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2802 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2803 si.nPage, si.nMin, si.nMax);
2805 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2806 memset(&si, 0, sizeof(si));
2807 si.cbSize = sizeof(si);
2808 si.fMask = SIF_PAGE | SIF_RANGE;
2809 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2810 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2811 "Vertical scrollbar is visible, should be invisible.\n");
2812 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2813 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2814 si.nPage, si.nMin, si.nMax);
2816 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2817 memset(&si, 0, sizeof(si));
2818 si.cbSize = sizeof(si);
2819 si.fMask = SIF_PAGE | SIF_RANGE;
2820 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2821 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2822 "Vertical scrollbar is visible, should be invisible.\n");
2823 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2824 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2825 si.nPage, si.nMin, si.nMax);
2827 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2828 memset(&si, 0, sizeof(si));
2829 si.cbSize = sizeof(si);
2830 si.fMask = SIF_PAGE | SIF_RANGE;
2831 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2832 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2833 "Vertical scrollbar is invisible, should be visible.\n");
2834 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2835 "reported page/range is %d (%d..%d)\n",
2836 si.nPage, si.nMin, si.nMax);
2838 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2839 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
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 visible, should be invisible.\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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2851 memset(&si, 0, sizeof(si));
2852 si.cbSize = sizeof(si);
2853 si.fMask = SIF_PAGE | SIF_RANGE;
2854 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2855 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2856 "Vertical scrollbar is visible, should be invisible.\n");
2857 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2858 "reported page/range is %d (%d..%d)\n",
2859 si.nPage, si.nMin, si.nMax);
2861 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2862 EM_SCROLL will make visible any forcefully invisible scrollbar */
2863 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2864 memset(&si, 0, sizeof(si));
2865 si.cbSize = sizeof(si);
2866 si.fMask = SIF_PAGE | SIF_RANGE;
2867 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2868 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2869 "Vertical scrollbar is invisible, should be visible.\n");
2870 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2871 "reported page/range is %d (%d..%d)\n",
2872 si.nPage, si.nMin, si.nMax);
2874 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2875 memset(&si, 0, sizeof(si));
2876 si.cbSize = sizeof(si);
2877 si.fMask = SIF_PAGE | SIF_RANGE;
2878 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2879 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2880 "Vertical scrollbar is visible, should be invisible.\n");
2881 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2882 "reported page/range is %d (%d..%d)\n",
2883 si.nPage, si.nMin, si.nMax);
2885 /* Again, EM_SCROLL, with SB_LINEUP */
2886 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
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 invisible, should be visible.\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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2898 memset(&si, 0, sizeof(si));
2899 si.cbSize = sizeof(si);
2900 si.fMask = SIF_PAGE | SIF_RANGE;
2901 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2902 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2903 "Vertical scrollbar is visible, should be invisible.\n");
2904 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2905 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2906 si.nPage, si.nMin, si.nMax);
2908 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2909 memset(&si, 0, sizeof(si));
2910 si.cbSize = sizeof(si);
2911 si.fMask = SIF_PAGE | SIF_RANGE;
2912 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2913 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2914 "Vertical scrollbar is invisible, should be visible.\n");
2915 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2916 "reported page/range is %d (%d..%d)\n",
2917 si.nPage, si.nMin, si.nMax);
2919 DestroyWindow(hwndRichEdit);
2922 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2923 hwndRichEdit = new_richedit(NULL);
2925 #define ENABLE_WS_VSCROLL(hwnd) \
2926 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2927 #define DISABLE_WS_VSCROLL(hwnd) \
2928 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2930 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2931 ENABLE_WS_VSCROLL(hwndRichEdit);
2932 memset(&si, 0, sizeof(si));
2933 si.cbSize = sizeof(si);
2934 si.fMask = SIF_PAGE | SIF_RANGE;
2935 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2936 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2937 "Vertical scrollbar is invisible, should be visible.\n");
2938 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2939 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2940 si.nPage, si.nMin, si.nMax);
2942 /* Ditto, see above */
2943 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
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, (LPARAM)"a");
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\na");
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, 0);
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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
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 visible, should be invisible.\n");
2998 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2999 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3000 si.nPage, si.nMin, si.nMax);
3002 DestroyWindow(hwndRichEdit);
3004 hwndRichEdit = new_richedit(NULL);
3006 DISABLE_WS_VSCROLL(hwndRichEdit);
3007 memset(&si, 0, sizeof(si));
3008 si.cbSize = sizeof(si);
3009 si.fMask = SIF_PAGE | SIF_RANGE;
3010 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3011 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3012 "Vertical scrollbar is visible, should be invisible.\n");
3013 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3014 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3015 si.nPage, si.nMin, si.nMax);
3017 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3018 memset(&si, 0, sizeof(si));
3019 si.cbSize = sizeof(si);
3020 si.fMask = SIF_PAGE | SIF_RANGE;
3021 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3022 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3023 "Vertical scrollbar is visible, should be invisible.\n");
3024 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3025 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3026 si.nPage, si.nMin, si.nMax);
3028 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3029 memset(&si, 0, sizeof(si));
3030 si.cbSize = sizeof(si);
3031 si.fMask = SIF_PAGE | SIF_RANGE;
3032 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3033 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3034 "Vertical scrollbar is visible, should be invisible.\n");
3035 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3036 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3037 si.nPage, si.nMin, si.nMax);
3039 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3040 memset(&si, 0, sizeof(si));
3041 si.cbSize = sizeof(si);
3042 si.fMask = SIF_PAGE | SIF_RANGE;
3043 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3044 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3045 "Vertical scrollbar is visible, should be invisible.\n");
3046 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3047 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3048 si.nPage, si.nMin, si.nMax);
3050 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3051 memset(&si, 0, sizeof(si));
3052 si.cbSize = sizeof(si);
3053 si.fMask = SIF_PAGE | SIF_RANGE;
3054 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3055 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3056 "Vertical scrollbar is invisible, should be visible.\n");
3057 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3058 "reported page/range is %d (%d..%d)\n",
3059 si.nPage, si.nMin, si.nMax);
3061 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3062 DISABLE_WS_VSCROLL(hwndRichEdit);
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 visible, should be invisible.\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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3074 memset(&si, 0, sizeof(si));
3075 si.cbSize = sizeof(si);
3076 si.fMask = SIF_PAGE | SIF_RANGE;
3077 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3078 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3079 "Vertical scrollbar is visible, should be invisible.\n");
3080 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3081 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3082 si.nPage, si.nMin, si.nMax);
3084 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3085 memset(&si, 0, sizeof(si));
3086 si.cbSize = sizeof(si);
3087 si.fMask = SIF_PAGE | SIF_RANGE;
3088 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3089 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3090 "Vertical scrollbar is invisible, should be visible.\n");
3091 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3092 "reported page/range is %d (%d..%d)\n",
3093 si.nPage, si.nMin, si.nMax);
3095 DISABLE_WS_VSCROLL(hwndRichEdit);
3096 memset(&si, 0, sizeof(si));
3097 si.cbSize = sizeof(si);
3098 si.fMask = SIF_PAGE | SIF_RANGE;
3099 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3100 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3101 "Vertical scrollbar is visible, should be invisible.\n");
3102 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3103 "reported page/range is %d (%d..%d)\n",
3104 si.nPage, si.nMin, si.nMax);
3106 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3107 EM_SCROLL will make visible any forcefully invisible scrollbar */
3108 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3109 memset(&si, 0, sizeof(si));
3110 si.cbSize = sizeof(si);
3111 si.fMask = SIF_PAGE | SIF_RANGE;
3112 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3113 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3114 "Vertical scrollbar is invisible, should be visible.\n");
3115 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3116 "reported page/range is %d (%d..%d)\n",
3117 si.nPage, si.nMin, si.nMax);
3119 DISABLE_WS_VSCROLL(hwndRichEdit);
3120 memset(&si, 0, sizeof(si));
3121 si.cbSize = sizeof(si);
3122 si.fMask = SIF_PAGE | SIF_RANGE;
3123 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3124 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3125 "Vertical scrollbar is visible, should be invisible.\n");
3126 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3127 "reported page/range is %d (%d..%d)\n",
3128 si.nPage, si.nMin, si.nMax);
3130 /* Again, EM_SCROLL, with SB_LINEUP */
3131 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
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 invisible, should be visible.\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 DestroyWindow(hwndRichEdit);
3144 /* This window proc models what is going on with Corman Lisp 3.0.
3145 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3146 force the scrollbar into visibility. Recursion should NOT happen
3147 as a result of this action.
3149 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3150 if (r) {
3151 richeditProc = cls.lpfnWndProc;
3152 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3153 cls.lpszClassName = "RicheditStupidOverride";
3154 if(!RegisterClassA(&cls)) assert(0);
3156 recursionLevel = 0;
3157 WM_SIZE_recursionLevel = 0;
3158 bailedOutOfRecursion = FALSE;
3159 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3160 ok(!bailedOutOfRecursion,
3161 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3163 recursionLevel = 0;
3164 WM_SIZE_recursionLevel = 0;
3165 bailedOutOfRecursion = FALSE;
3166 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3167 ok(!bailedOutOfRecursion,
3168 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3170 /* Unblock window in order to process WM_DESTROY */
3171 recursionLevel = 0;
3172 bailedOutOfRecursion = FALSE;
3173 WM_SIZE_recursionLevel = 0;
3174 DestroyWindow(hwndRichEdit);
3178 static void test_EM_SETUNDOLIMIT(void)
3180 /* cases we test for:
3181 * default behaviour - limiting at 100 undo's
3182 * undo disabled - setting a limit of 0
3183 * undo limited - undo limit set to some to some number, like 2
3184 * bad input - sending a negative number should default to 100 undo's */
3186 HWND hwndRichEdit = new_richedit(NULL);
3187 CHARRANGE cr;
3188 int i;
3189 int result;
3191 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3192 cr.cpMin = 0;
3193 cr.cpMax = 1;
3194 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3195 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3196 also, multiple pastes don't combine like WM_CHAR would */
3197 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3199 /* first case - check the default */
3200 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3201 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3202 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3203 for (i=0; i<100; i++) /* Undo 100 of them */
3204 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3205 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3206 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3208 /* second case - cannot undo */
3209 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3210 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3211 SendMessage(hwndRichEdit,
3212 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3213 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3214 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3216 /* third case - set it to an arbitrary number */
3217 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3218 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3219 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3220 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3221 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3222 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3223 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3224 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3225 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3226 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3227 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3228 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3229 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3230 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3232 /* fourth case - setting negative numbers should default to 100 undos */
3233 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3234 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3235 ok (result == 100,
3236 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3238 DestroyWindow(hwndRichEdit);
3241 static void test_ES_PASSWORD(void)
3243 /* This isn't hugely testable, so we're just going to run it through its paces */
3245 HWND hwndRichEdit = new_richedit(NULL);
3246 WCHAR result;
3248 /* First, check the default of a regular control */
3249 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3250 ok (result == 0,
3251 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3253 /* Now, set it to something normal */
3254 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3255 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3256 ok (result == 120,
3257 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3259 /* Now, set it to something odd */
3260 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3261 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3262 ok (result == 1234,
3263 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3264 DestroyWindow(hwndRichEdit);
3267 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3268 LPBYTE pbBuff,
3269 LONG cb,
3270 LONG *pcb)
3272 char** str = (char**)dwCookie;
3273 *pcb = cb;
3274 if (*pcb > 0) {
3275 memcpy(*str, pbBuff, *pcb);
3276 *str += *pcb;
3278 return 0;
3281 static void test_WM_SETTEXT(void)
3283 HWND hwndRichEdit = new_richedit(NULL);
3284 const char * TestItem1 = "TestSomeText";
3285 const char * TestItem2 = "TestSomeText\r";
3286 const char * TestItem2_after = "TestSomeText\r\n";
3287 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3288 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3289 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3290 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3291 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3292 const char * TestItem5_after = "TestSomeText TestSomeText";
3293 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3294 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3295 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3296 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3298 const char rtftextA[] = "{\\rtf sometext}";
3299 const char urtftextA[] = "{\\urtf sometext}";
3300 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3301 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3302 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3304 char buf[1024] = {0};
3305 WCHAR bufW[1024] = {0};
3306 LRESULT result;
3308 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3309 any solitary \r to be converted to \r\n on return. Properly paired
3310 \r\n are not affected. It also shows that the special sequence \r\r\n
3311 gets converted to a single space.
3314 #define TEST_SETTEXT(a, b) \
3315 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3316 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3317 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3318 ok (result == lstrlen(buf), \
3319 "WM_GETTEXT returned %ld instead of expected %u\n", \
3320 result, lstrlen(buf)); \
3321 result = strcmp(b, buf); \
3322 ok(result == 0, \
3323 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3325 TEST_SETTEXT(TestItem1, TestItem1)
3326 TEST_SETTEXT(TestItem2, TestItem2_after)
3327 TEST_SETTEXT(TestItem3, TestItem3_after)
3328 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3329 TEST_SETTEXT(TestItem4, TestItem4_after)
3330 TEST_SETTEXT(TestItem5, TestItem5_after)
3331 TEST_SETTEXT(TestItem6, TestItem6_after)
3332 TEST_SETTEXT(TestItem7, TestItem7_after)
3334 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3335 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3336 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3337 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3338 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3339 DestroyWindow(hwndRichEdit);
3340 #undef TEST_SETTEXT
3342 #define TEST_SETTEXTW(a, b) \
3343 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3344 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3345 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3346 ok (result == lstrlenW(bufW), \
3347 "WM_GETTEXT returned %ld instead of expected %u\n", \
3348 result, lstrlenW(bufW)); \
3349 result = lstrcmpW(b, bufW); \
3350 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3352 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3353 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3354 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3355 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3356 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3357 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3358 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3359 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3360 DestroyWindow(hwndRichEdit);
3361 #undef TEST_SETTEXTW
3364 static void test_EM_STREAMOUT(void)
3366 HWND hwndRichEdit = new_richedit(NULL);
3367 int r;
3368 EDITSTREAM es;
3369 char buf[1024] = {0};
3370 char * p;
3372 const char * TestItem1 = "TestSomeText";
3373 const char * TestItem2 = "TestSomeText\r";
3374 const char * TestItem3 = "TestSomeText\r\n";
3376 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3377 p = buf;
3378 es.dwCookie = (DWORD_PTR)&p;
3379 es.dwError = 0;
3380 es.pfnCallback = test_WM_SETTEXT_esCallback;
3381 memset(buf, 0, sizeof(buf));
3382 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3383 r = strlen(buf);
3384 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3385 ok(strcmp(buf, TestItem1) == 0,
3386 "streamed text different, got %s\n", buf);
3388 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3389 p = buf;
3390 es.dwCookie = (DWORD_PTR)&p;
3391 es.dwError = 0;
3392 es.pfnCallback = test_WM_SETTEXT_esCallback;
3393 memset(buf, 0, sizeof(buf));
3394 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3395 r = strlen(buf);
3396 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3397 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3398 ok(strcmp(buf, TestItem3) == 0,
3399 "streamed text different from, got %s\n", buf);
3400 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3401 p = buf;
3402 es.dwCookie = (DWORD_PTR)&p;
3403 es.dwError = 0;
3404 es.pfnCallback = test_WM_SETTEXT_esCallback;
3405 memset(buf, 0, sizeof(buf));
3406 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3407 r = strlen(buf);
3408 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3409 ok(strcmp(buf, TestItem3) == 0,
3410 "streamed text different, got %s\n", buf);
3412 DestroyWindow(hwndRichEdit);
3415 static void test_EM_STREAMOUT_FONTTBL(void)
3417 HWND hwndRichEdit = new_richedit(NULL);
3418 EDITSTREAM es;
3419 char buf[1024] = {0};
3420 char * p;
3421 char * fontTbl;
3422 int brackCount;
3424 const char * TestItem = "TestSomeText";
3426 /* fills in the richedit control with some text */
3427 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3429 /* streams out the text in rtf format */
3430 p = buf;
3431 es.dwCookie = (DWORD_PTR)&p;
3432 es.dwError = 0;
3433 es.pfnCallback = test_WM_SETTEXT_esCallback;
3434 memset(buf, 0, sizeof(buf));
3435 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3437 /* scans for \fonttbl, error if not found */
3438 fontTbl = strstr(buf, "\\fonttbl");
3439 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3440 if(fontTbl)
3442 /* scans for terminating closing bracket */
3443 brackCount = 1;
3444 while(*fontTbl && brackCount)
3446 if(*fontTbl == '{')
3447 brackCount++;
3448 else if(*fontTbl == '}')
3449 brackCount--;
3450 fontTbl++;
3452 /* checks whether closing bracket is ok */
3453 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3454 if(!brackCount)
3456 /* char before closing fonttbl block should be a closed bracket */
3457 fontTbl -= 2;
3458 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3460 /* char after fonttbl block should be a crlf */
3461 fontTbl += 2;
3462 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3465 DestroyWindow(hwndRichEdit);
3469 static void test_EM_SETTEXTEX(void)
3471 HWND hwndRichEdit, parent;
3472 SCROLLINFO si;
3473 int sel_start, sel_end;
3474 SETTEXTEX setText;
3475 GETTEXTEX getText;
3476 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3477 'S', 'o', 'm', 'e',
3478 'T', 'e', 'x', 't', 0};
3479 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3480 't', 'S', 'o', 'm',
3481 'e', 'T', 'e', 'x',
3482 't', 't', 'S', 'o',
3483 'm', 'e', 'T', 'e',
3484 'x', 't', 0};
3485 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3486 '\r','t','S','o','m','e','T','e','x','t',0};
3487 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3488 'S', 'o', 'm', 'e',
3489 'T', 'e', 'x', 't',
3490 '\r', 0};
3491 const char * TestItem2_after = "TestSomeText\r\n";
3492 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3493 'S', 'o', 'm', 'e',
3494 'T', 'e', 'x', 't',
3495 '\r','\n','\r','\n', 0};
3496 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3497 'S', 'o', 'm', 'e',
3498 'T', 'e', 'x', 't',
3499 '\n','\n', 0};
3500 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3501 'S', 'o', 'm', 'e',
3502 'T', 'e', 'x', 't',
3503 '\r','\r', 0};
3504 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3505 'S', 'o', 'm', 'e',
3506 'T', 'e', 'x', 't',
3507 '\r','\r','\n','\r',
3508 '\n', 0};
3509 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3510 'S', 'o', 'm', 'e',
3511 'T', 'e', 'x', 't',
3512 ' ','\r', 0};
3513 #define MAX_BUF_LEN 1024
3514 WCHAR buf[MAX_BUF_LEN];
3515 char bufACP[MAX_BUF_LEN];
3516 char * p;
3517 int result;
3518 CHARRANGE cr;
3519 EDITSTREAM es;
3520 WNDCLASSA cls;
3522 /* Test the scroll position with and without a parent window.
3524 * For some reason the scroll position is 0 after EM_SETTEXTEX
3525 * with the ST_SELECTION flag only when the control has a parent
3526 * window, even though the selection is at the end. */
3527 cls.style = 0;
3528 cls.lpfnWndProc = DefWindowProcA;
3529 cls.cbClsExtra = 0;
3530 cls.cbWndExtra = 0;
3531 cls.hInstance = GetModuleHandleA(0);
3532 cls.hIcon = 0;
3533 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3534 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3535 cls.lpszMenuName = NULL;
3536 cls.lpszClassName = "ParentTestClass";
3537 if(!RegisterClassA(&cls)) assert(0);
3539 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3540 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3541 ok (parent != 0, "Failed to create parent window\n");
3543 hwndRichEdit = CreateWindowEx(0,
3544 RICHEDIT_CLASS, NULL,
3545 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3546 0, 0, 200, 60, parent, NULL,
3547 hmoduleRichEdit, NULL);
3549 setText.codepage = CP_ACP;
3550 setText.flags = ST_SELECTION;
3551 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3552 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3553 si.cbSize = sizeof(si);
3554 si.fMask = SIF_ALL;
3555 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3556 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3557 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3558 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3559 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3561 DestroyWindow(parent);
3563 /* Test without a parent window */
3564 hwndRichEdit = new_richedit(NULL);
3565 setText.codepage = CP_ACP;
3566 setText.flags = ST_SELECTION;
3567 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3568 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3569 si.cbSize = sizeof(si);
3570 si.fMask = SIF_ALL;
3571 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3572 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3573 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3574 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3575 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3577 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3578 * but this time it is because the selection is at the beginning. */
3579 setText.codepage = CP_ACP;
3580 setText.flags = ST_DEFAULT;
3581 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3582 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3583 si.cbSize = sizeof(si);
3584 si.fMask = SIF_ALL;
3585 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3586 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3587 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3588 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3589 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3591 setText.codepage = 1200; /* no constant for unicode */
3592 getText.codepage = 1200; /* no constant for unicode */
3593 getText.cb = MAX_BUF_LEN;
3594 getText.flags = GT_DEFAULT;
3595 getText.lpDefaultChar = NULL;
3596 getText.lpUsedDefChar = NULL;
3598 setText.flags = 0;
3599 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3600 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3601 ok(lstrcmpW(buf, TestItem1) == 0,
3602 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3604 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3605 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3607 setText.codepage = 1200; /* no constant for unicode */
3608 getText.codepage = 1200; /* no constant for unicode */
3609 getText.cb = MAX_BUF_LEN;
3610 getText.flags = GT_DEFAULT;
3611 getText.lpDefaultChar = NULL;
3612 getText.lpUsedDefChar = NULL;
3613 setText.flags = 0;
3614 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3615 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3616 ok(lstrcmpW(buf, TestItem2) == 0,
3617 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3619 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3620 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3621 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3622 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3624 /* Baseline test for just-enough buffer space for string */
3625 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3626 getText.codepage = 1200; /* no constant for unicode */
3627 getText.flags = GT_DEFAULT;
3628 getText.lpDefaultChar = NULL;
3629 getText.lpUsedDefChar = NULL;
3630 memset(buf, 0, MAX_BUF_LEN);
3631 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3632 ok(lstrcmpW(buf, TestItem2) == 0,
3633 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3635 /* When there is enough space for one character, but not both, of the CRLF
3636 pair at the end of the string, the CR is not copied at all. That is,
3637 the caller must not see CRLF pairs truncated to CR at the end of the
3638 string.
3640 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3641 getText.codepage = 1200; /* no constant for unicode */
3642 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3643 getText.lpDefaultChar = NULL;
3644 getText.lpUsedDefChar = NULL;
3645 memset(buf, 0, MAX_BUF_LEN);
3646 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3647 ok(lstrcmpW(buf, TestItem1) == 0,
3648 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3651 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3652 setText.codepage = 1200; /* no constant for unicode */
3653 getText.codepage = 1200; /* no constant for unicode */
3654 getText.cb = MAX_BUF_LEN;
3655 getText.flags = GT_DEFAULT;
3656 getText.lpDefaultChar = NULL;
3657 getText.lpUsedDefChar = NULL;
3658 setText.flags = 0;
3659 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3660 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3661 ok(lstrcmpW(buf, TestItem3_after) == 0,
3662 "EM_SETTEXTEX did not convert properly\n");
3664 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3665 setText.codepage = 1200; /* no constant for unicode */
3666 getText.codepage = 1200; /* no constant for unicode */
3667 getText.cb = MAX_BUF_LEN;
3668 getText.flags = GT_DEFAULT;
3669 getText.lpDefaultChar = NULL;
3670 getText.lpUsedDefChar = NULL;
3671 setText.flags = 0;
3672 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3673 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3674 ok(lstrcmpW(buf, TestItem3_after) == 0,
3675 "EM_SETTEXTEX did not convert properly\n");
3677 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3678 setText.codepage = 1200; /* no constant for unicode */
3679 getText.codepage = 1200; /* no constant for unicode */
3680 getText.cb = MAX_BUF_LEN;
3681 getText.flags = GT_DEFAULT;
3682 getText.lpDefaultChar = NULL;
3683 getText.lpUsedDefChar = NULL;
3684 setText.flags = 0;
3685 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3686 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3687 ok(lstrcmpW(buf, TestItem4_after) == 0,
3688 "EM_SETTEXTEX did not convert properly\n");
3690 /* !ST_SELECTION && Unicode && !\rtf */
3691 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3692 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3694 ok (result == 1,
3695 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3696 ok(lstrlenW(buf) == 0,
3697 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3699 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3700 setText.flags = 0;
3701 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3702 /* select some text */
3703 cr.cpMax = 1;
3704 cr.cpMin = 3;
3705 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3706 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3707 setText.flags = ST_SELECTION;
3708 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3709 ok(result == 0,
3710 "EM_SETTEXTEX with NULL lParam to replace selection"
3711 " with no text should return 0. Got %i\n",
3712 result);
3714 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3715 setText.flags = 0;
3716 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3717 /* select some text */
3718 cr.cpMax = 1;
3719 cr.cpMin = 3;
3720 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3721 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3722 setText.flags = ST_SELECTION;
3723 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3724 (WPARAM)&setText, (LPARAM) TestItem1);
3725 /* get text */
3726 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3727 ok(result == lstrlenW(TestItem1),
3728 "EM_SETTEXTEX with NULL lParam to replace selection"
3729 " with no text should return 0. Got %i\n",
3730 result);
3731 ok(lstrlenW(buf) == 22,
3732 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3733 lstrlenW(buf) );
3735 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3736 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3737 p = (char *)buf;
3738 es.dwCookie = (DWORD_PTR)&p;
3739 es.dwError = 0;
3740 es.pfnCallback = test_WM_SETTEXT_esCallback;
3741 memset(buf, 0, sizeof(buf));
3742 SendMessage(hwndRichEdit, EM_STREAMOUT,
3743 (WPARAM)(SF_RTF), (LPARAM)&es);
3744 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3746 /* !ST_SELECTION && !Unicode && \rtf */
3747 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3748 getText.codepage = 1200; /* no constant for unicode */
3749 getText.cb = MAX_BUF_LEN;
3750 getText.flags = GT_DEFAULT;
3751 getText.lpDefaultChar = NULL;
3752 getText.lpUsedDefChar = NULL;
3754 setText.flags = 0;
3755 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3756 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3757 ok(lstrcmpW(buf, TestItem1) == 0,
3758 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3760 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3761 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3762 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3763 getText.codepage = CP_ACP;
3764 getText.cb = MAX_BUF_LEN;
3765 getText.flags = GT_DEFAULT;
3766 getText.lpDefaultChar = NULL;
3767 getText.lpUsedDefChar = NULL;
3769 setText.flags = ST_SELECTION;
3770 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3771 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3772 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3773 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3774 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3776 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3777 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3778 p = (char *)buf;
3779 es.dwCookie = (DWORD_PTR)&p;
3780 es.dwError = 0;
3781 es.pfnCallback = test_WM_SETTEXT_esCallback;
3782 memset(buf, 0, sizeof(buf));
3783 SendMessage(hwndRichEdit, EM_STREAMOUT,
3784 (WPARAM)(SF_RTF), (LPARAM)&es);
3785 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3787 /* select some text */
3788 cr.cpMax = 1;
3789 cr.cpMin = 3;
3790 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3792 /* ST_SELECTION && !Unicode && \rtf */
3793 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3794 getText.codepage = 1200; /* no constant for unicode */
3795 getText.cb = MAX_BUF_LEN;
3796 getText.flags = GT_DEFAULT;
3797 getText.lpDefaultChar = NULL;
3798 getText.lpUsedDefChar = NULL;
3800 setText.flags = ST_SELECTION;
3801 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3802 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3803 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3805 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3806 setText.codepage = 1200; /* no constant for unicode */
3807 getText.codepage = CP_ACP;
3808 getText.cb = MAX_BUF_LEN;
3810 setText.flags = 0;
3811 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3812 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3814 /* select some text */
3815 cr.cpMax = 1;
3816 cr.cpMin = 3;
3817 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3819 /* ST_SELECTION && !Unicode && !\rtf */
3820 setText.codepage = CP_ACP;
3821 getText.codepage = 1200; /* no constant for unicode */
3822 getText.cb = MAX_BUF_LEN;
3823 getText.flags = GT_DEFAULT;
3824 getText.lpDefaultChar = NULL;
3825 getText.lpUsedDefChar = NULL;
3827 setText.flags = ST_SELECTION;
3828 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3829 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3830 ok(lstrcmpW(buf, TestItem1alt) == 0,
3831 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3832 " using ST_SELECTION and non-Unicode\n");
3834 /* Test setting text using rich text format */
3835 setText.flags = 0;
3836 setText.codepage = CP_ACP;
3837 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3838 getText.codepage = CP_ACP;
3839 getText.cb = MAX_BUF_LEN;
3840 getText.flags = GT_DEFAULT;
3841 getText.lpDefaultChar = NULL;
3842 getText.lpUsedDefChar = NULL;
3843 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3844 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3846 setText.flags = 0;
3847 setText.codepage = CP_ACP;
3848 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3849 getText.codepage = CP_ACP;
3850 getText.cb = MAX_BUF_LEN;
3851 getText.flags = GT_DEFAULT;
3852 getText.lpDefaultChar = NULL;
3853 getText.lpUsedDefChar = NULL;
3854 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3855 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3857 DestroyWindow(hwndRichEdit);
3860 static void test_EM_LIMITTEXT(void)
3862 int ret;
3864 HWND hwndRichEdit = new_richedit(NULL);
3866 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3867 * about setting the length to -1 for multiline edit controls doesn't happen.
3870 /* Don't check default gettextlimit case. That's done in other tests */
3872 /* Set textlimit to 100 */
3873 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3874 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3875 ok (ret == 100,
3876 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3878 /* Set textlimit to 0 */
3879 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3880 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3881 ok (ret == 65536,
3882 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3884 /* Set textlimit to -1 */
3885 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3886 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3887 ok (ret == -1,
3888 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3890 /* Set textlimit to -2 */
3891 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3892 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3893 ok (ret == -2,
3894 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3896 DestroyWindow (hwndRichEdit);
3900 static void test_EM_EXLIMITTEXT(void)
3902 int i, selBegin, selEnd, len1, len2;
3903 int result;
3904 char text[1024 + 1];
3905 char buffer[1024 + 1];
3906 int textlimit = 0; /* multiple of 100 */
3907 HWND hwndRichEdit = new_richedit(NULL);
3909 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3910 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3912 textlimit = 256000;
3913 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3914 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3915 /* set higher */
3916 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3918 textlimit = 1000;
3919 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3920 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3921 /* set lower */
3922 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3924 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3925 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3926 /* default for WParam = 0 */
3927 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3929 textlimit = sizeof(text)-1;
3930 memset(text, 'W', textlimit);
3931 text[sizeof(text)-1] = 0;
3932 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3933 /* maxed out text */
3934 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3936 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3937 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3938 len1 = selEnd - selBegin;
3940 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3941 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3942 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3943 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3944 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3945 len2 = selEnd - selBegin;
3947 ok(len1 != len2,
3948 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3949 len1,len2,i);
3951 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3952 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3953 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3954 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3955 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3956 len1 = selEnd - selBegin;
3958 ok(len1 != len2,
3959 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3960 len1,len2,i);
3962 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3963 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3964 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3965 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3966 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3967 len2 = selEnd - selBegin;
3969 ok(len1 == len2,
3970 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3971 len1,len2,i);
3973 /* set text up to the limit, select all the text, then add a char */
3974 textlimit = 5;
3975 memset(text, 'W', textlimit);
3976 text[textlimit] = 0;
3977 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3978 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3979 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3980 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3981 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3982 result = strcmp(buffer, "A");
3983 ok(0 == result, "got string = \"%s\"\n", buffer);
3985 /* WM_SETTEXT not limited */
3986 textlimit = 10;
3987 memset(text, 'W', textlimit);
3988 text[textlimit] = 0;
3989 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3990 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3991 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3992 i = strlen(buffer);
3993 ok(10 == i, "expected 10 chars\n");
3994 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3995 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3997 /* try inserting more text at end */
3998 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3999 ok(0 == i, "WM_CHAR wasn't processed\n");
4000 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4001 i = strlen(buffer);
4002 ok(10 == i, "expected 10 chars, got %i\n", i);
4003 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4004 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4006 /* try inserting text at beginning */
4007 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4008 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4009 ok(0 == i, "WM_CHAR wasn't processed\n");
4010 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4011 i = strlen(buffer);
4012 ok(10 == i, "expected 10 chars, got %i\n", i);
4013 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4014 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4016 /* WM_CHAR is limited */
4017 textlimit = 1;
4018 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4019 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4020 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4021 ok(0 == i, "WM_CHAR wasn't processed\n");
4022 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4023 ok(0 == i, "WM_CHAR wasn't processed\n");
4024 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4025 i = strlen(buffer);
4026 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4028 DestroyWindow(hwndRichEdit);
4031 static void test_EM_GETLIMITTEXT(void)
4033 int i;
4034 HWND hwndRichEdit = new_richedit(NULL);
4036 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4037 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4039 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4040 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4041 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4043 DestroyWindow(hwndRichEdit);
4046 static void test_WM_SETFONT(void)
4048 /* There is no invalid input or error conditions for this function.
4049 * NULL wParam and lParam just fall back to their default values
4050 * It should be noted that even if you use a gibberish name for your fonts
4051 * here, it will still work because the name is stored. They will display as
4052 * System, but will report their name to be whatever they were created as */
4054 HWND hwndRichEdit = new_richedit(NULL);
4055 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4056 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4057 FF_DONTCARE, "Marlett");
4058 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4059 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4060 FF_DONTCARE, "MS Sans Serif");
4061 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4062 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4063 FF_DONTCARE, "Courier");
4064 LOGFONTA sentLogFont;
4065 CHARFORMAT2A returnedCF2A;
4067 returnedCF2A.cbSize = sizeof(returnedCF2A);
4069 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4070 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4071 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4073 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4074 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4075 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4076 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4078 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4079 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4080 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4081 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4082 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4083 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4085 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4086 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4087 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4088 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4089 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4090 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4092 /* This last test is special since we send in NULL. We clear the variables
4093 * and just compare to "System" instead of the sent in font name. */
4094 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4095 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4096 returnedCF2A.cbSize = sizeof(returnedCF2A);
4098 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4099 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4100 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4101 ok (!strcmp("System",returnedCF2A.szFaceName),
4102 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4104 DestroyWindow(hwndRichEdit);
4108 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4109 LPBYTE pbBuff,
4110 LONG cb,
4111 LONG *pcb)
4113 const char** str = (const char**)dwCookie;
4114 int size = strlen(*str);
4115 if(size > 3) /* let's make it piecemeal for fun */
4116 size = 3;
4117 *pcb = cb;
4118 if (*pcb > size) {
4119 *pcb = size;
4121 if (*pcb > 0) {
4122 memcpy(pbBuff, *str, *pcb);
4123 *str += *pcb;
4125 return 0;
4128 static void test_EM_GETMODIFY(void)
4130 HWND hwndRichEdit = new_richedit(NULL);
4131 LRESULT result;
4132 SETTEXTEX setText;
4133 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4134 'S', 'o', 'm', 'e',
4135 'T', 'e', 'x', 't', 0};
4136 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4137 'S', 'o', 'm', 'e',
4138 'O', 't', 'h', 'e', 'r',
4139 'T', 'e', 'x', 't', 0};
4140 const char* streamText = "hello world";
4141 CHARFORMAT2 cf2;
4142 PARAFORMAT2 pf2;
4143 EDITSTREAM es;
4145 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4146 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4147 FF_DONTCARE, "Courier");
4149 setText.codepage = 1200; /* no constant for unicode */
4150 setText.flags = ST_KEEPUNDO;
4153 /* modify flag shouldn't be set when richedit is first created */
4154 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4155 ok (result == 0,
4156 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4158 /* setting modify flag should actually set it */
4159 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4160 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4161 ok (result != 0,
4162 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4164 /* clearing modify flag should actually clear it */
4165 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4166 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4167 ok (result == 0,
4168 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4170 /* setting font doesn't change modify flag */
4171 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4172 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4173 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4174 ok (result == 0,
4175 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4177 /* setting text should set modify flag */
4178 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4179 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4180 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4181 ok (result != 0,
4182 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4184 /* undo previous text doesn't reset modify flag */
4185 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4186 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4187 ok (result != 0,
4188 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4190 /* set text with no flag to keep undo stack should not set modify flag */
4191 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4192 setText.flags = 0;
4193 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4194 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4195 ok (result == 0,
4196 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4198 /* WM_SETTEXT doesn't modify */
4199 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4200 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4201 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4202 ok (result == 0,
4203 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4205 /* clear the text */
4206 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4207 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4208 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4209 ok (result == 0,
4210 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4212 /* replace text */
4213 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4214 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4215 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4216 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4217 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4218 ok (result != 0,
4219 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4221 /* copy/paste text 1 */
4222 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4223 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4224 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4225 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4226 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4227 ok (result != 0,
4228 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4230 /* copy/paste text 2 */
4231 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4232 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4233 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4234 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4235 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4236 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4237 ok (result != 0,
4238 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4240 /* press char */
4241 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4242 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4243 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4244 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4245 ok (result != 0,
4246 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4248 /* press del */
4249 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4250 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4251 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4252 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4253 ok (result != 0,
4254 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4256 /* set char format */
4257 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4258 cf2.cbSize = sizeof(CHARFORMAT2);
4259 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4260 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4261 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4262 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4263 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4264 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4265 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4266 ok (result != 0,
4267 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4269 /* set para format */
4270 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4271 pf2.cbSize = sizeof(PARAFORMAT2);
4272 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4273 (LPARAM) &pf2);
4274 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4275 pf2.wAlignment = PFA_RIGHT;
4276 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4277 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4278 ok (result == 0,
4279 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4281 /* EM_STREAM */
4282 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4283 es.dwCookie = (DWORD_PTR)&streamText;
4284 es.dwError = 0;
4285 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4286 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4287 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4288 ok (result != 0,
4289 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4291 DestroyWindow(hwndRichEdit);
4294 struct exsetsel_s {
4295 LONG min;
4296 LONG max;
4297 LRESULT expected_retval;
4298 int expected_getsel_start;
4299 int expected_getsel_end;
4300 int _getsel_todo_wine;
4303 const struct exsetsel_s exsetsel_tests[] = {
4304 /* sanity tests */
4305 {5, 10, 10, 5, 10, 0},
4306 {15, 17, 17, 15, 17, 0},
4307 /* test cpMax > strlen() */
4308 {0, 100, 18, 0, 18, 1},
4309 /* test cpMin == cpMax */
4310 {5, 5, 5, 5, 5, 0},
4311 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4312 {-1, 0, 5, 5, 5, 0},
4313 {-1, 17, 5, 5, 5, 0},
4314 {-1, 18, 5, 5, 5, 0},
4315 /* test cpMin < 0 && cpMax < 0 */
4316 {-1, -1, 17, 17, 17, 0},
4317 {-4, -5, 17, 17, 17, 0},
4318 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4319 {0, -1, 18, 0, 18, 1},
4320 {17, -5, 18, 17, 18, 1},
4321 {18, -3, 17, 17, 17, 0},
4322 /* test if cpMin > cpMax */
4323 {15, 19, 18, 15, 18, 1},
4324 {19, 15, 18, 15, 18, 1}
4327 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4328 CHARRANGE cr;
4329 LRESULT result;
4330 int start, end;
4332 cr.cpMin = setsel->min;
4333 cr.cpMax = setsel->max;
4334 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4336 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4338 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4340 if (setsel->_getsel_todo_wine) {
4341 todo_wine {
4342 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);
4344 } else {
4345 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);
4349 static void test_EM_EXSETSEL(void)
4351 HWND hwndRichEdit = new_richedit(NULL);
4352 int i;
4353 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4355 /* sending some text to the window */
4356 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4357 /* 01234567890123456*/
4358 /* 10 */
4360 for (i = 0; i < num_tests; i++) {
4361 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4364 DestroyWindow(hwndRichEdit);
4367 static void test_EM_REPLACESEL(int redraw)
4369 HWND hwndRichEdit = new_richedit(NULL);
4370 char buffer[1024] = {0};
4371 int r;
4372 GETTEXTEX getText;
4373 CHARRANGE cr;
4375 /* sending some text to the window */
4376 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4377 /* 01234567890123456*/
4378 /* 10 */
4380 /* FIXME add more tests */
4381 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4382 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4383 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4384 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4385 r = strcmp(buffer, "testing");
4386 ok(0 == r, "expected %d, got %d\n", 0, r);
4388 DestroyWindow(hwndRichEdit);
4390 hwndRichEdit = new_richedit(NULL);
4392 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4393 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4395 /* Test behavior with carriage returns and newlines */
4396 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4397 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4398 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4399 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4400 r = strcmp(buffer, "RichEdit1");
4401 ok(0 == r, "expected %d, got %d\n", 0, r);
4402 getText.cb = 1024;
4403 getText.codepage = CP_ACP;
4404 getText.flags = GT_DEFAULT;
4405 getText.lpDefaultChar = NULL;
4406 getText.lpUsedDefChar = NULL;
4407 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4408 ok(strcmp(buffer, "RichEdit1") == 0,
4409 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4411 /* Test number of lines reported after EM_REPLACESEL */
4412 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4413 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4415 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4416 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4417 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4418 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4419 r = strcmp(buffer, "RichEdit1\r\n");
4420 ok(0 == r, "expected %d, got %d\n", 0, r);
4421 getText.cb = 1024;
4422 getText.codepage = CP_ACP;
4423 getText.flags = GT_DEFAULT;
4424 getText.lpDefaultChar = NULL;
4425 getText.lpUsedDefChar = NULL;
4426 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4427 ok(strcmp(buffer, "RichEdit1\r") == 0,
4428 "EM_GETTEXTEX returned incorrect string\n");
4430 /* Test number of lines reported after EM_REPLACESEL */
4431 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4432 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4434 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4435 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4436 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4438 /* Test number of lines reported after EM_REPLACESEL */
4439 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4440 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4442 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4443 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4444 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4445 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4447 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4448 r = strcmp(buffer, "RichEdit1\r\n");
4449 ok(0 == r, "expected %d, got %d\n", 0, r);
4450 getText.cb = 1024;
4451 getText.codepage = CP_ACP;
4452 getText.flags = GT_DEFAULT;
4453 getText.lpDefaultChar = NULL;
4454 getText.lpUsedDefChar = NULL;
4455 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4456 ok(strcmp(buffer, "RichEdit1\r") == 0,
4457 "EM_GETTEXTEX returned incorrect string\n");
4459 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4460 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4461 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4462 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4464 /* The following tests show that richedit should handle the special \r\r\n
4465 sequence by turning it into a single space on insertion. However,
4466 EM_REPLACESEL on WinXP returns the number of characters in the original
4467 string.
4470 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4471 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4472 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4473 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4474 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4475 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4476 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4478 /* Test the actual string */
4479 getText.cb = 1024;
4480 getText.codepage = CP_ACP;
4481 getText.flags = GT_DEFAULT;
4482 getText.lpDefaultChar = NULL;
4483 getText.lpUsedDefChar = NULL;
4484 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4485 ok(strcmp(buffer, "\r\r") == 0,
4486 "EM_GETTEXTEX returned incorrect string\n");
4488 /* Test number of lines reported after EM_REPLACESEL */
4489 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4490 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4492 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4493 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4494 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4495 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4496 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4497 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4498 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4500 /* Test the actual string */
4501 getText.cb = 1024;
4502 getText.codepage = CP_ACP;
4503 getText.flags = GT_DEFAULT;
4504 getText.lpDefaultChar = NULL;
4505 getText.lpUsedDefChar = NULL;
4506 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4507 ok(strcmp(buffer, " ") == 0,
4508 "EM_GETTEXTEX returned incorrect string\n");
4510 /* Test number of lines reported after EM_REPLACESEL */
4511 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4512 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4514 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4515 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4516 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4517 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4518 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4519 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4520 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4522 /* Test the actual string */
4523 getText.cb = 1024;
4524 getText.codepage = CP_ACP;
4525 getText.flags = GT_DEFAULT;
4526 getText.lpDefaultChar = NULL;
4527 getText.lpUsedDefChar = NULL;
4528 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4529 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4530 "EM_GETTEXTEX returned incorrect string\n");
4532 /* Test number of lines reported after EM_REPLACESEL */
4533 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4534 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4536 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4537 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4538 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4539 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4540 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4541 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4542 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4544 /* Test the actual string */
4545 getText.cb = 1024;
4546 getText.codepage = CP_ACP;
4547 getText.flags = GT_DEFAULT;
4548 getText.lpDefaultChar = NULL;
4549 getText.lpUsedDefChar = NULL;
4550 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4551 ok(strcmp(buffer, " \r") == 0,
4552 "EM_GETTEXTEX returned incorrect string\n");
4554 /* Test number of lines reported after EM_REPLACESEL */
4555 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4556 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4558 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4559 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4560 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4561 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4562 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4563 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4564 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4566 /* Test the actual string */
4567 getText.cb = 1024;
4568 getText.codepage = CP_ACP;
4569 getText.flags = GT_DEFAULT;
4570 getText.lpDefaultChar = NULL;
4571 getText.lpUsedDefChar = NULL;
4572 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4573 ok(strcmp(buffer, " \r\r") == 0,
4574 "EM_GETTEXTEX returned incorrect string\n");
4576 /* Test number of lines reported after EM_REPLACESEL */
4577 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4578 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4580 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4581 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4582 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4583 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4584 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4585 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4586 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4588 /* Test the actual string */
4589 getText.cb = 1024;
4590 getText.codepage = CP_ACP;
4591 getText.flags = GT_DEFAULT;
4592 getText.lpDefaultChar = NULL;
4593 getText.lpUsedDefChar = NULL;
4594 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4595 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4596 "EM_GETTEXTEX returned incorrect string\n");
4598 /* Test number of lines reported after EM_REPLACESEL */
4599 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4600 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4602 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4603 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4604 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4605 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4606 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4607 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4608 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4610 /* Test the actual string */
4611 getText.cb = 1024;
4612 getText.codepage = CP_ACP;
4613 getText.flags = GT_DEFAULT;
4614 getText.lpDefaultChar = NULL;
4615 getText.lpUsedDefChar = NULL;
4616 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4617 ok(strcmp(buffer, "\r\r") == 0,
4618 "EM_GETTEXTEX returned incorrect string\n");
4620 /* Test number of lines reported after EM_REPLACESEL */
4621 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4622 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4624 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4625 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4626 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4627 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4628 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4629 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4630 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4632 /* Test the actual string */
4633 getText.cb = 1024;
4634 getText.codepage = CP_ACP;
4635 getText.flags = GT_DEFAULT;
4636 getText.lpDefaultChar = NULL;
4637 getText.lpUsedDefChar = NULL;
4638 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4639 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4640 "EM_GETTEXTEX returned incorrect string\n");
4642 /* Test number of lines reported after EM_REPLACESEL */
4643 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4644 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4646 if (!redraw)
4647 /* This is needed to avoid interferring with keybd_event calls
4648 * on other tests that simulate keyboard events. */
4649 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4651 DestroyWindow(hwndRichEdit);
4654 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4655 * to test the state of the modifiers (Ctrl/Alt/Shift).
4657 * Therefore Ctrl-<key> keystrokes need to be simulated with
4658 * keybd_event or by using SetKeyboardState to set the modifiers
4659 * and SendMessage to simulate the keystrokes.
4661 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
4663 LRESULT result;
4664 hold_key(VK_CONTROL);
4665 result = SendMessage(hwnd, WM_KEYDOWN, key, 1);
4666 release_key(VK_CONTROL);
4667 return result;
4670 static void test_WM_PASTE(void)
4672 int result;
4673 char buffer[1024] = {0};
4674 const char* text1 = "testing paste\r";
4675 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4676 const char* text1_after = "testing paste\r\n";
4677 const char* text2 = "testing paste\r\rtesting paste";
4678 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4679 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4680 HWND hwndRichEdit = new_richedit(NULL);
4682 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4683 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4685 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4686 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4687 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4688 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4689 /* Pasted text should be visible at this step */
4690 result = strcmp(text1_step1, buffer);
4691 ok(result == 0,
4692 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4694 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4695 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4696 /* Text should be the same as before (except for \r -> \r\n conversion) */
4697 result = strcmp(text1_after, buffer);
4698 ok(result == 0,
4699 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4701 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4702 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4703 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4704 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4705 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4706 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4707 /* Pasted text should be visible at this step */
4708 result = strcmp(text3, buffer);
4709 ok(result == 0,
4710 "test paste: strcmp = %i\n", result);
4711 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4712 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4713 /* Text should be the same as before (except for \r -> \r\n conversion) */
4714 result = strcmp(text2_after, buffer);
4715 ok(result == 0,
4716 "test paste: strcmp = %i\n", result);
4717 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
4718 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4719 /* Text should revert to post-paste state */
4720 result = strcmp(buffer,text3);
4721 ok(result == 0,
4722 "test paste: strcmp = %i\n", result);
4724 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4725 /* Send WM_CHAR to simulates Ctrl-V */
4726 SendMessage(hwndRichEdit, WM_CHAR, 22,
4727 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4728 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4729 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4730 result = strcmp(buffer,"");
4731 ok(result == 0,
4732 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4734 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4735 * with SetKeyboard state. */
4737 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4738 /* Simulates paste (Ctrl-V) */
4739 hold_key(VK_CONTROL);
4740 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4741 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4742 release_key(VK_CONTROL);
4743 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4744 result = strcmp(buffer,"paste");
4745 ok(result == 0,
4746 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4748 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4749 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4750 /* Simulates copy (Ctrl-C) */
4751 hold_key(VK_CONTROL);
4752 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4753 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) | 1);
4754 release_key(VK_CONTROL);
4755 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4756 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4757 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4758 result = strcmp(buffer,"testing");
4759 ok(result == 0,
4760 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4762 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4763 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4764 /* Simulates select all (Ctrl-A) */
4765 hold_key(VK_CONTROL);
4766 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4767 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) | 1);
4768 /* Simulates select cut (Ctrl-X) */
4769 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4770 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) | 1);
4771 release_key(VK_CONTROL);
4772 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4773 result = strcmp(buffer,"");
4774 ok(result == 0,
4775 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4776 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4777 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4778 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4779 result = strcmp(buffer,"cut\r\n");
4780 todo_wine ok(result == 0,
4781 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4782 /* Simulates undo (Ctrl-Z) */
4783 hold_key(VK_CONTROL);
4784 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4785 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4786 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4787 result = strcmp(buffer,"");
4788 ok(result == 0,
4789 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4790 /* Simulates redo (Ctrl-Y) */
4791 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4792 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4793 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4794 result = strcmp(buffer,"cut\r\n");
4795 todo_wine ok(result == 0,
4796 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4797 release_key(VK_CONTROL);
4799 DestroyWindow(hwndRichEdit);
4802 static void test_EM_FORMATRANGE(void)
4804 int r, i, tpp_x, tpp_y;
4805 HDC hdc;
4806 HWND hwndRichEdit = new_richedit(NULL);
4807 FORMATRANGE fr;
4808 BOOL skip_non_english;
4809 static const struct {
4810 const char *string; /* The string */
4811 int first; /* First 'pagebreak', 0 for don't care */
4812 int second; /* Second 'pagebreak', 0 for don't care */
4813 } fmtstrings[] = {
4814 {"WINE wine", 0, 0},
4815 {"WINE wineWine", 0, 0},
4816 {"WINE\r\nwine\r\nwine", 5, 10},
4817 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4818 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4821 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4822 if (skip_non_english)
4823 skip("Skipping some tests on non-English platform\n");
4825 hdc = GetDC(hwndRichEdit);
4826 ok(hdc != NULL, "Could not get HDC\n");
4828 /* Calculate the twips per pixel */
4829 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4830 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4832 /* Test the simple case where all the text fits in the page rect. */
4833 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4834 fr.hdc = fr.hdcTarget = hdc;
4835 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4836 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4837 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4838 fr.chrg.cpMin = 0;
4839 fr.chrg.cpMax = -1;
4840 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4841 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4843 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4844 fr.rc.bottom = fr.rcPage.bottom;
4845 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4846 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4848 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4850 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4852 GETTEXTLENGTHEX gtl;
4853 SIZE stringsize;
4854 int len;
4856 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4858 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4859 gtl.codepage = CP_ACP;
4860 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4862 /* Get some size information for the string */
4863 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4865 /* Define the box to be half the width needed and a bit larger than the height.
4866 * Changes to the width means we have at least 2 pages. Changes to the height
4867 * is done so we can check the changing of fr.rc.bottom.
4869 fr.hdc = fr.hdcTarget = hdc;
4870 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4871 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4872 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4874 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4875 todo_wine {
4876 ok(r == len, "Expected %d, got %d\n", len, r);
4879 /* We know that the page can't hold the full string. See how many characters
4880 * are on the first one
4882 fr.chrg.cpMin = 0;
4883 fr.chrg.cpMax = -1;
4884 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4885 todo_wine {
4886 if (! skip_non_english)
4887 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4889 if (fmtstrings[i].first)
4890 todo_wine {
4891 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4893 else
4894 ok(r < len, "Expected < %d, got %d\n", len, r);
4896 /* Do another page */
4897 fr.chrg.cpMin = r;
4898 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4899 if (fmtstrings[i].second)
4900 todo_wine {
4901 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4903 else if (! skip_non_english)
4904 ok (r < len, "Expected < %d, got %d\n", len, r);
4906 /* There is at least on more page, but we don't care */
4908 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4909 todo_wine {
4910 ok(r == len, "Expected %d, got %d\n", len, r);
4914 ReleaseDC(NULL, hdc);
4915 DestroyWindow(hwndRichEdit);
4918 static int nCallbackCount = 0;
4920 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4921 LONG cb, LONG* pcb)
4923 const char text[] = {'t','e','s','t'};
4925 if (sizeof(text) <= cb)
4927 if ((int)dwCookie != nCallbackCount)
4929 *pcb = 0;
4930 return 0;
4933 memcpy (pbBuff, text, sizeof(text));
4934 *pcb = sizeof(text);
4936 nCallbackCount++;
4938 return 0;
4940 else
4941 return 1; /* indicates callback failed */
4944 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4945 LPBYTE pbBuff,
4946 LONG cb,
4947 LONG *pcb)
4949 const char** str = (const char**)dwCookie;
4950 int size = strlen(*str);
4951 *pcb = cb;
4952 if (*pcb > size) {
4953 *pcb = size;
4955 if (*pcb > 0) {
4956 memcpy(pbBuff, *str, *pcb);
4957 *str += *pcb;
4959 return 0;
4962 struct StringWithLength {
4963 int length;
4964 char *buffer;
4967 /* This callback is used to handled the null characters in a string. */
4968 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4969 LPBYTE pbBuff,
4970 LONG cb,
4971 LONG *pcb)
4973 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4974 int size = str->length;
4975 *pcb = cb;
4976 if (*pcb > size) {
4977 *pcb = size;
4979 if (*pcb > 0) {
4980 memcpy(pbBuff, str->buffer, *pcb);
4981 str->buffer += *pcb;
4982 str->length -= *pcb;
4984 return 0;
4987 static void test_EM_STREAMIN(void)
4989 HWND hwndRichEdit = new_richedit(NULL);
4990 LRESULT result;
4991 EDITSTREAM es;
4992 char buffer[1024] = {0};
4994 const char * streamText0 = "{\\rtf1 TestSomeText}";
4995 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4996 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4998 const char * streamText1 =
4999 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5000 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5001 "}\r\n";
5003 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5004 const char * streamText2 =
5005 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5006 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5007 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5008 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5009 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5010 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5011 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5013 const char * streamText3 = "RichEdit1";
5015 const char * streamText4 =
5016 "This text just needs to be long enough to cause run to be split onto "
5017 "two separate lines and make sure the null terminating character is "
5018 "handled properly.\0";
5019 int length4 = strlen(streamText4) + 1;
5020 struct StringWithLength cookieForStream4 = {
5021 length4,
5022 (char *)streamText4,
5025 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5026 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5027 struct StringWithLength cookieForStream5 = {
5028 sizeof(streamText5),
5029 (char *)streamText5,
5032 /* Minimal test without \par at the end */
5033 es.dwCookie = (DWORD_PTR)&streamText0;
5034 es.dwError = 0;
5035 es.pfnCallback = test_EM_STREAMIN_esCallback;
5036 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5037 ok(result == 12, "got %ld, expected %d\n", result, 12);
5039 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5040 ok (result == 12,
5041 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5042 result = strcmp (buffer,"TestSomeText");
5043 ok (result == 0,
5044 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5045 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5047 /* Native richedit 2.0 ignores last \par */
5048 es.dwCookie = (DWORD_PTR)&streamText0a;
5049 es.dwError = 0;
5050 es.pfnCallback = test_EM_STREAMIN_esCallback;
5051 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5052 ok(result == 12, "got %ld, expected %d\n", result, 12);
5054 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5055 ok (result == 12,
5056 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5057 result = strcmp (buffer,"TestSomeText");
5058 ok (result == 0,
5059 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5060 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5062 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5063 es.dwCookie = (DWORD_PTR)&streamText0b;
5064 es.dwError = 0;
5065 es.pfnCallback = test_EM_STREAMIN_esCallback;
5066 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5067 ok(result == 13, "got %ld, expected %d\n", result, 13);
5069 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5070 ok (result == 14,
5071 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5072 result = strcmp (buffer,"TestSomeText\r\n");
5073 ok (result == 0,
5074 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5075 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5077 es.dwCookie = (DWORD_PTR)&streamText1;
5078 es.dwError = 0;
5079 es.pfnCallback = test_EM_STREAMIN_esCallback;
5080 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5081 ok(result == 12, "got %ld, expected %d\n", result, 12);
5083 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5084 ok (result == 12,
5085 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5086 result = strcmp (buffer,"TestSomeText");
5087 ok (result == 0,
5088 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5089 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5091 es.dwCookie = (DWORD_PTR)&streamText2;
5092 es.dwError = 0;
5093 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5094 ok(result == 0, "got %ld, expected %d\n", result, 0);
5096 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5097 ok (result == 0,
5098 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5099 ok (strlen(buffer) == 0,
5100 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5101 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5103 es.dwCookie = (DWORD_PTR)&streamText3;
5104 es.dwError = 0;
5105 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5106 ok(result == 0, "got %ld, expected %d\n", result, 0);
5108 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5109 ok (result == 0,
5110 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5111 ok (strlen(buffer) == 0,
5112 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5113 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5115 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5116 es.dwError = 0;
5117 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5118 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5119 ok(result == length4, "got %ld, expected %d\n", result, length4);
5121 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5122 ok (result == length4,
5123 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5124 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5126 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5127 es.dwError = 0;
5128 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5129 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5130 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5132 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5133 ok (result == length5,
5134 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length5);
5135 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5137 DestroyWindow(hwndRichEdit);
5140 static void test_EM_StreamIn_Undo(void)
5142 /* The purpose of this test is to determine when a EM_StreamIn should be
5143 * undoable. This is important because WM_PASTE currently uses StreamIn and
5144 * pasting should always be undoable but streaming isn't always.
5146 * cases to test:
5147 * StreamIn plain text without SFF_SELECTION.
5148 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5149 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5150 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5151 * Feel free to add tests for other text modes or StreamIn things.
5155 HWND hwndRichEdit = new_richedit(NULL);
5156 LRESULT result;
5157 EDITSTREAM es;
5158 char buffer[1024] = {0};
5159 const char randomtext[] = "Some text";
5161 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5163 /* StreamIn, no SFF_SELECTION */
5164 es.dwCookie = nCallbackCount;
5165 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5166 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5167 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5168 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5169 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5170 result = strcmp (buffer,"test");
5171 ok (result == 0,
5172 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5174 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5175 ok (result == FALSE,
5176 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5178 /* StreamIn, SFF_SELECTION, but nothing selected */
5179 es.dwCookie = nCallbackCount;
5180 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5181 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5182 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5183 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5184 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5185 result = strcmp (buffer,"testSome text");
5186 ok (result == 0,
5187 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5189 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5190 ok (result == TRUE,
5191 "EM_STREAMIN with SFF_SELECTION but no selection set "
5192 "should create an undo\n");
5194 /* StreamIn, SFF_SELECTION, with a selection */
5195 es.dwCookie = nCallbackCount;
5196 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5197 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5198 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5199 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5200 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5201 result = strcmp (buffer,"Sometesttext");
5202 ok (result == 0,
5203 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5205 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5206 ok (result == TRUE,
5207 "EM_STREAMIN with SFF_SELECTION and selection set "
5208 "should create an undo\n");
5210 DestroyWindow(hwndRichEdit);
5213 static BOOL is_em_settextex_supported(HWND hwnd)
5215 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5216 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5219 static void test_unicode_conversions(void)
5221 static const WCHAR tW[] = {'t',0};
5222 static const WCHAR teW[] = {'t','e',0};
5223 static const WCHAR textW[] = {'t','e','s','t',0};
5224 static const char textA[] = "test";
5225 char bufA[64];
5226 WCHAR bufW[64];
5227 HWND hwnd;
5228 int em_settextex_supported, ret;
5230 #define set_textA(hwnd, wm_set_text, txt) \
5231 do { \
5232 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5233 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5234 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5235 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5236 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5237 } while(0)
5238 #define expect_textA(hwnd, wm_get_text, txt) \
5239 do { \
5240 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5241 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5242 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5243 memset(bufA, 0xAA, sizeof(bufA)); \
5244 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5245 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5246 ret = lstrcmpA(bufA, txt); \
5247 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5248 } while(0)
5250 #define set_textW(hwnd, wm_set_text, txt) \
5251 do { \
5252 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5253 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5254 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5255 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5256 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5257 } while(0)
5258 #define expect_textW(hwnd, wm_get_text, txt) \
5259 do { \
5260 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5261 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5262 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5263 memset(bufW, 0xAA, sizeof(bufW)); \
5264 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5265 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5266 ret = lstrcmpW(bufW, txt); \
5267 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5268 } while(0)
5269 #define expect_empty(hwnd, wm_get_text) \
5270 do { \
5271 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5272 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5273 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5274 memset(bufA, 0xAA, sizeof(bufA)); \
5275 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5276 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5277 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5278 } while(0)
5280 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5281 0, 0, 200, 60, 0, 0, 0, 0);
5282 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5284 ret = IsWindowUnicode(hwnd);
5285 ok(ret, "RichEdit20W should be unicode under NT\n");
5287 /* EM_SETTEXTEX is supported starting from version 3.0 */
5288 em_settextex_supported = is_em_settextex_supported(hwnd);
5289 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5290 em_settextex_supported ? "" : "NOT ");
5292 expect_empty(hwnd, WM_GETTEXT);
5293 expect_empty(hwnd, EM_GETTEXTEX);
5295 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5296 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5297 expect_textA(hwnd, WM_GETTEXT, "t");
5298 expect_textA(hwnd, EM_GETTEXTEX, "t");
5299 expect_textW(hwnd, EM_GETTEXTEX, tW);
5301 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5302 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5303 expect_textA(hwnd, WM_GETTEXT, "te");
5304 expect_textA(hwnd, EM_GETTEXTEX, "te");
5305 expect_textW(hwnd, EM_GETTEXTEX, teW);
5307 set_textA(hwnd, WM_SETTEXT, NULL);
5308 expect_empty(hwnd, WM_GETTEXT);
5309 expect_empty(hwnd, EM_GETTEXTEX);
5311 set_textA(hwnd, WM_SETTEXT, textA);
5312 expect_textA(hwnd, WM_GETTEXT, textA);
5313 expect_textA(hwnd, EM_GETTEXTEX, textA);
5314 expect_textW(hwnd, EM_GETTEXTEX, textW);
5316 if (em_settextex_supported)
5318 set_textA(hwnd, EM_SETTEXTEX, textA);
5319 expect_textA(hwnd, WM_GETTEXT, textA);
5320 expect_textA(hwnd, EM_GETTEXTEX, textA);
5321 expect_textW(hwnd, EM_GETTEXTEX, textW);
5324 set_textW(hwnd, WM_SETTEXT, textW);
5325 expect_textW(hwnd, WM_GETTEXT, textW);
5326 expect_textA(hwnd, WM_GETTEXT, textA);
5327 expect_textW(hwnd, EM_GETTEXTEX, textW);
5328 expect_textA(hwnd, EM_GETTEXTEX, textA);
5330 if (em_settextex_supported)
5332 set_textW(hwnd, EM_SETTEXTEX, textW);
5333 expect_textW(hwnd, WM_GETTEXT, textW);
5334 expect_textA(hwnd, WM_GETTEXT, textA);
5335 expect_textW(hwnd, EM_GETTEXTEX, textW);
5336 expect_textA(hwnd, EM_GETTEXTEX, textA);
5338 DestroyWindow(hwnd);
5340 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5341 0, 0, 200, 60, 0, 0, 0, 0);
5342 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5344 ret = IsWindowUnicode(hwnd);
5345 ok(!ret, "RichEdit20A should NOT be unicode\n");
5347 set_textA(hwnd, WM_SETTEXT, textA);
5348 expect_textA(hwnd, WM_GETTEXT, textA);
5349 expect_textA(hwnd, EM_GETTEXTEX, textA);
5350 expect_textW(hwnd, EM_GETTEXTEX, textW);
5352 if (em_settextex_supported)
5354 set_textA(hwnd, EM_SETTEXTEX, textA);
5355 expect_textA(hwnd, WM_GETTEXT, textA);
5356 expect_textA(hwnd, EM_GETTEXTEX, textA);
5357 expect_textW(hwnd, EM_GETTEXTEX, textW);
5360 set_textW(hwnd, WM_SETTEXT, textW);
5361 expect_textW(hwnd, WM_GETTEXT, textW);
5362 expect_textA(hwnd, WM_GETTEXT, textA);
5363 expect_textW(hwnd, EM_GETTEXTEX, textW);
5364 expect_textA(hwnd, EM_GETTEXTEX, textA);
5366 if (em_settextex_supported)
5368 set_textW(hwnd, EM_SETTEXTEX, textW);
5369 expect_textW(hwnd, WM_GETTEXT, textW);
5370 expect_textA(hwnd, WM_GETTEXT, textA);
5371 expect_textW(hwnd, EM_GETTEXTEX, textW);
5372 expect_textA(hwnd, EM_GETTEXTEX, textA);
5374 DestroyWindow(hwnd);
5377 static void test_WM_CHAR(void)
5379 HWND hwnd;
5380 int ret;
5381 const char * char_list = "abc\rabc\r";
5382 const char * expected_content_single = "abcabc";
5383 const char * expected_content_multi = "abc\r\nabc\r\n";
5384 char buffer[64] = {0};
5385 const char * p;
5387 /* single-line control must IGNORE carriage returns */
5388 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5389 0, 0, 200, 60, 0, 0, 0, 0);
5390 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5392 p = char_list;
5393 while (*p != '\0') {
5394 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5395 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5396 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5397 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5398 p++;
5401 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5402 ret = strcmp(buffer, expected_content_single);
5403 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5405 DestroyWindow(hwnd);
5407 /* multi-line control inserts CR normally */
5408 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5409 0, 0, 200, 60, 0, 0, 0, 0);
5410 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5412 p = char_list;
5413 while (*p != '\0') {
5414 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5415 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5416 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5417 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5418 p++;
5421 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5422 ret = strcmp(buffer, expected_content_multi);
5423 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5425 DestroyWindow(hwnd);
5428 static void test_EM_GETTEXTLENGTHEX(void)
5430 HWND hwnd;
5431 GETTEXTLENGTHEX gtl;
5432 int ret;
5433 const char * base_string = "base string";
5434 const char * test_string = "a\nb\n\n\r\n";
5435 const char * test_string_after = "a";
5436 const char * test_string_2 = "a\rtest\rstring";
5437 char buffer[64] = {0};
5439 /* single line */
5440 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5441 0, 0, 200, 60, 0, 0, 0, 0);
5442 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5444 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5445 gtl.codepage = CP_ACP;
5446 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5447 ok(ret == 0, "ret %d\n",ret);
5449 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5450 gtl.codepage = CP_ACP;
5451 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5452 ok(ret == 0, "ret %d\n",ret);
5454 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5456 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5457 gtl.codepage = CP_ACP;
5458 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5459 ok(ret == strlen(base_string), "ret %d\n",ret);
5461 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5462 gtl.codepage = CP_ACP;
5463 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5464 ok(ret == strlen(base_string), "ret %d\n",ret);
5466 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5468 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5469 gtl.codepage = CP_ACP;
5470 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5471 ok(ret == 1, "ret %d\n",ret);
5473 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5474 gtl.codepage = CP_ACP;
5475 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5476 ok(ret == 1, "ret %d\n",ret);
5478 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5479 ret = strcmp(buffer, test_string_after);
5480 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5482 DestroyWindow(hwnd);
5484 /* multi line */
5485 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5486 0, 0, 200, 60, 0, 0, 0, 0);
5487 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5489 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5490 gtl.codepage = CP_ACP;
5491 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5492 ok(ret == 0, "ret %d\n",ret);
5494 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5495 gtl.codepage = CP_ACP;
5496 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5497 ok(ret == 0, "ret %d\n",ret);
5499 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5501 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5502 gtl.codepage = CP_ACP;
5503 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5504 ok(ret == strlen(base_string), "ret %d\n",ret);
5506 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5507 gtl.codepage = CP_ACP;
5508 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5509 ok(ret == strlen(base_string), "ret %d\n",ret);
5511 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5513 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5514 gtl.codepage = CP_ACP;
5515 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5516 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5518 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5519 gtl.codepage = CP_ACP;
5520 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5521 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5523 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5525 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5526 gtl.codepage = CP_ACP;
5527 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5528 ok(ret == 10, "ret %d\n",ret);
5530 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5531 gtl.codepage = CP_ACP;
5532 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5533 ok(ret == 6, "ret %d\n",ret);
5535 /* Unicode/NUMCHARS/NUMBYTES */
5536 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5538 gtl.flags = GTL_DEFAULT;
5539 gtl.codepage = 1200;
5540 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5541 ok(ret == lstrlen(test_string_2),
5542 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlen(test_string_2));
5544 gtl.flags = GTL_NUMCHARS;
5545 gtl.codepage = 1200;
5546 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5547 ok(ret == lstrlen(test_string_2),
5548 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlen(test_string_2));
5550 gtl.flags = GTL_NUMBYTES;
5551 gtl.codepage = 1200;
5552 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5553 ok(ret == lstrlen(test_string_2)*2,
5554 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5556 gtl.flags = GTL_PRECISE;
5557 gtl.codepage = 1200;
5558 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5559 ok(ret == lstrlen(test_string_2)*2,
5560 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5562 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5563 gtl.codepage = 1200;
5564 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5565 ok(ret == lstrlen(test_string_2),
5566 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2));
5568 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5569 gtl.codepage = 1200;
5570 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5571 ok(ret == E_INVALIDARG,
5572 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5574 DestroyWindow(hwnd);
5578 /* globals that parent and child access when checking event masks & notifications */
5579 static HWND eventMaskEditHwnd = 0;
5580 static int queriedEventMask;
5581 static int watchForEventMask = 0;
5583 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5584 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5586 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5588 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5590 return DefWindowProcA(hwnd, message, wParam, lParam);
5593 /* test event masks in combination with WM_COMMAND */
5594 static void test_eventMask(void)
5596 HWND parent;
5597 int ret, style;
5598 WNDCLASSA cls;
5599 const char text[] = "foo bar\n";
5600 int eventMask;
5602 /* register class to capture WM_COMMAND */
5603 cls.style = 0;
5604 cls.lpfnWndProc = ParentMsgCheckProcA;
5605 cls.cbClsExtra = 0;
5606 cls.cbWndExtra = 0;
5607 cls.hInstance = GetModuleHandleA(0);
5608 cls.hIcon = 0;
5609 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5610 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5611 cls.lpszMenuName = NULL;
5612 cls.lpszClassName = "EventMaskParentClass";
5613 if(!RegisterClassA(&cls)) assert(0);
5615 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5616 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5617 ok (parent != 0, "Failed to create parent window\n");
5619 eventMaskEditHwnd = new_richedit(parent);
5620 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5622 eventMask = ENM_CHANGE | ENM_UPDATE;
5623 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5624 ok(ret == ENM_NONE, "wrong event mask\n");
5625 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5626 ok(ret == eventMask, "failed to set event mask\n");
5628 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5629 queriedEventMask = 0; /* initialize to something other than we expect */
5630 watchForEventMask = EN_CHANGE;
5631 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5632 ok(ret == TRUE, "failed to set text\n");
5633 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5634 notification in response to WM_SETTEXT */
5635 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5636 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5638 /* check to see if EN_CHANGE is sent when redraw is turned off */
5639 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5640 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5641 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5642 /* redraw is disabled by making the window invisible. */
5643 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5644 queriedEventMask = 0; /* initialize to something other than we expect */
5645 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5646 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5647 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5648 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5649 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5651 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5652 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5653 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5654 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5655 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5656 watchForEventMask = EN_UPDATE;
5657 queriedEventMask = 0; /* initialize to something other than we expect */
5658 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5659 ok(queriedEventMask == 0,
5660 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5661 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5662 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5663 queriedEventMask = 0; /* initialize to something other than we expect */
5664 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5665 ok(queriedEventMask == eventMask,
5666 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5669 DestroyWindow(parent);
5672 static int received_WM_NOTIFY = 0;
5673 static int modify_at_WM_NOTIFY = 0;
5674 static BOOL filter_on_WM_NOTIFY = FALSE;
5675 static HWND hwndRichedit_WM_NOTIFY;
5677 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5679 if(message == WM_NOTIFY)
5681 received_WM_NOTIFY = 1;
5682 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5683 if (filter_on_WM_NOTIFY) return TRUE;
5685 return DefWindowProcA(hwnd, message, wParam, lParam);
5688 static void test_WM_NOTIFY(void)
5690 HWND parent;
5691 WNDCLASSA cls;
5692 CHARFORMAT2 cf2;
5693 int sel_start, sel_end;
5695 /* register class to capture WM_NOTIFY */
5696 cls.style = 0;
5697 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5698 cls.cbClsExtra = 0;
5699 cls.cbWndExtra = 0;
5700 cls.hInstance = GetModuleHandleA(0);
5701 cls.hIcon = 0;
5702 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5703 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5704 cls.lpszMenuName = NULL;
5705 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5706 if(!RegisterClassA(&cls)) assert(0);
5708 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5709 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5710 ok (parent != 0, "Failed to create parent window\n");
5712 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5713 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5715 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5717 /* Notifications for selection change should only be sent when selection
5718 actually changes. EM_SETCHARFORMAT is one message that calls
5719 ME_CommitUndo, which should check whether message should be sent */
5720 received_WM_NOTIFY = 0;
5721 cf2.cbSize = sizeof(CHARFORMAT2);
5722 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5723 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5724 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5725 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5726 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5728 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5729 already at 0. */
5730 received_WM_NOTIFY = 0;
5731 modify_at_WM_NOTIFY = 0;
5732 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5733 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5734 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5736 received_WM_NOTIFY = 0;
5737 modify_at_WM_NOTIFY = 0;
5738 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5739 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5741 received_WM_NOTIFY = 0;
5742 modify_at_WM_NOTIFY = 0;
5743 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5744 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5745 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5747 /* Test for WM_NOTIFY messages with redraw disabled. */
5748 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5749 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5750 received_WM_NOTIFY = 0;
5751 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5752 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5753 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5755 /* Test filtering key events. */
5756 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5757 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5758 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5759 received_WM_NOTIFY = 0;
5760 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5761 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5762 ok(sel_start == 1 && sel_end == 1,
5763 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5764 filter_on_WM_NOTIFY = TRUE;
5765 received_WM_NOTIFY = 0;
5766 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5767 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5768 ok(sel_start == 1 && sel_end == 1,
5769 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5771 /* test with owner set to NULL */
5772 SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5773 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5774 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5775 ok(sel_start == 1 && sel_end == 1,
5776 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5778 DestroyWindow(hwndRichedit_WM_NOTIFY);
5779 DestroyWindow(parent);
5782 static void test_undo_coalescing(void)
5784 HWND hwnd;
5785 int result;
5786 char buffer[64] = {0};
5788 /* multi-line control inserts CR normally */
5789 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5790 0, 0, 200, 60, 0, 0, 0, 0);
5791 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5793 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5794 ok (result == FALSE, "Can undo after window creation.\n");
5795 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5796 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5797 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5798 ok (result == FALSE, "Can redo after window creation.\n");
5799 result = SendMessage(hwnd, EM_REDO, 0, 0);
5800 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5802 /* Test the effect of arrows keys during typing on undo transactions*/
5803 simulate_typing_characters(hwnd, "one two three");
5804 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5805 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5806 simulate_typing_characters(hwnd, " four five six");
5808 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5809 ok (result == FALSE, "Can redo before anything is undone.\n");
5810 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5811 ok (result == TRUE, "Cannot undo typed characters.\n");
5812 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5813 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5814 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5815 ok (result == TRUE, "Cannot redo after undo.\n");
5816 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5817 result = strcmp(buffer, "one two three");
5818 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5820 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5821 ok (result == TRUE, "Cannot undo typed characters.\n");
5822 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5823 ok (result == TRUE, "Failed to undo typed characters.\n");
5824 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5825 result = strcmp(buffer, "");
5826 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5828 /* Test the effect of focus changes during typing on undo transactions*/
5829 simulate_typing_characters(hwnd, "one two three");
5830 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5831 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5832 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5833 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5834 simulate_typing_characters(hwnd, " four five six");
5835 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5836 ok (result == TRUE, "Failed to undo typed characters.\n");
5837 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5838 result = strcmp(buffer, "one two three");
5839 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5841 /* Test the effect of the back key during typing on undo transactions */
5842 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5843 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5844 ok (result == TRUE, "Failed to clear the text.\n");
5845 simulate_typing_characters(hwnd, "one two threa");
5846 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5847 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5848 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5849 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5850 simulate_typing_characters(hwnd, "e four five six");
5851 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5852 ok (result == TRUE, "Failed to undo typed characters.\n");
5853 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5854 result = strcmp(buffer, "");
5855 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5857 /* Test the effect of the delete key during typing on undo transactions */
5858 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5859 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5860 ok(result == TRUE, "Failed to set the text.\n");
5861 SendMessage(hwnd, EM_SETSEL, 1, 1);
5862 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5863 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5864 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5865 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5866 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5867 ok (result == TRUE, "Failed to undo typed characters.\n");
5868 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5869 result = strcmp(buffer, "acd");
5870 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5871 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5872 ok (result == TRUE, "Failed to undo typed characters.\n");
5873 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5874 result = strcmp(buffer, "abcd");
5875 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5877 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5878 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5879 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5880 ok (result == TRUE, "Failed to clear the text.\n");
5881 simulate_typing_characters(hwnd, "one two three");
5882 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5883 ok (result == 0, "expected %d but got %d\n", 0, result);
5884 simulate_typing_characters(hwnd, " four five six");
5885 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5886 ok (result == TRUE, "Failed to undo typed characters.\n");
5887 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5888 result = strcmp(buffer, "one two three");
5889 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5890 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5891 ok (result == TRUE, "Failed to undo typed characters.\n");
5892 ok (result == TRUE, "Failed to undo typed characters.\n");
5893 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5894 result = strcmp(buffer, "");
5895 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5897 DestroyWindow(hwnd);
5900 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5902 int length;
5904 /* MSDN lied, length is actually the number of bytes. */
5905 length = bytes / sizeof(WCHAR);
5906 switch(code)
5908 case WB_ISDELIMITER:
5909 return text[pos] == 'X';
5910 case WB_LEFT:
5911 case WB_MOVEWORDLEFT:
5912 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5913 return pos-1;
5914 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5915 case WB_LEFTBREAK:
5916 pos--;
5917 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5918 pos--;
5919 return pos;
5920 case WB_RIGHT:
5921 case WB_MOVEWORDRIGHT:
5922 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5923 return pos+1;
5924 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5925 case WB_RIGHTBREAK:
5926 pos++;
5927 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5928 pos++;
5929 return pos;
5930 default:
5931 ok(FALSE, "Unexpected code %d\n", code);
5932 break;
5934 return 0;
5937 static void test_word_movement(void)
5939 HWND hwnd;
5940 int result;
5941 int sel_start, sel_end;
5942 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5944 /* multi-line control inserts CR normally */
5945 hwnd = new_richedit(NULL);
5947 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5948 ok (result == TRUE, "Failed to clear the text.\n");
5949 SendMessage(hwnd, EM_SETSEL, 0, 0);
5950 /* |one two three */
5952 send_ctrl_key(hwnd, VK_RIGHT);
5953 /* one |two three */
5954 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5955 ok(sel_start == sel_end, "Selection should be empty\n");
5956 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5958 send_ctrl_key(hwnd, VK_RIGHT);
5959 /* one two |three */
5960 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5961 ok(sel_start == sel_end, "Selection should be empty\n");
5962 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5964 send_ctrl_key(hwnd, VK_LEFT);
5965 /* one |two three */
5966 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5967 ok(sel_start == sel_end, "Selection should be empty\n");
5968 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5970 send_ctrl_key(hwnd, VK_LEFT);
5971 /* |one two three */
5972 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5973 ok(sel_start == sel_end, "Selection should be empty\n");
5974 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5976 SendMessage(hwnd, EM_SETSEL, 8, 8);
5977 /* one two | three */
5978 send_ctrl_key(hwnd, VK_RIGHT);
5979 /* one two |three */
5980 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5981 ok(sel_start == sel_end, "Selection should be empty\n");
5982 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5984 SendMessage(hwnd, EM_SETSEL, 11, 11);
5985 /* one two th|ree */
5986 send_ctrl_key(hwnd, VK_LEFT);
5987 /* one two |three */
5988 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5989 ok(sel_start == sel_end, "Selection should be empty\n");
5990 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5992 /* Test with a custom word break procedure that uses X as the delimiter. */
5993 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5994 ok (result == TRUE, "Failed to clear the text.\n");
5995 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5996 /* |one twoXthree */
5997 send_ctrl_key(hwnd, VK_RIGHT);
5998 /* one twoX|three */
5999 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6000 ok(sel_start == sel_end, "Selection should be empty\n");
6001 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6003 DestroyWindow(hwnd);
6005 /* Make sure the behaviour is the same with a unicode richedit window,
6006 * and using unicode functions. */
6008 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6009 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6010 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6012 /* Test with a custom word break procedure that uses X as the delimiter. */
6013 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6014 ok (result == TRUE, "Failed to clear the text.\n");
6015 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6016 /* |one twoXthree */
6017 send_ctrl_key(hwnd, VK_RIGHT);
6018 /* one twoX|three */
6019 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6020 ok(sel_start == sel_end, "Selection should be empty\n");
6021 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6023 DestroyWindow(hwnd);
6026 static void test_EM_CHARFROMPOS(void)
6028 HWND hwnd;
6029 int result;
6030 RECT rcClient;
6031 POINTL point;
6032 point.x = 0;
6033 point.y = 40;
6035 /* multi-line control inserts CR normally */
6036 hwnd = new_richedit(NULL);
6037 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6038 (LPARAM)"one two three four five six seven\reight");
6039 ok(result == 1, "Expected 1, got %d\n", result);
6040 GetClientRect(hwnd, &rcClient);
6042 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6043 ok(result == 34, "expected character index of 34 but got %d\n", result);
6045 /* Test with points outside the bounds of the richedit control. */
6046 point.x = -1;
6047 point.y = 40;
6048 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6049 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6051 point.x = 1000;
6052 point.y = 0;
6053 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6054 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6056 point.x = 1000;
6057 point.y = 36;
6058 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6059 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6061 point.x = 1000;
6062 point.y = -1;
6063 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6064 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6066 point.x = 1000;
6067 point.y = rcClient.bottom + 1;
6068 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6069 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6071 point.x = 1000;
6072 point.y = rcClient.bottom;
6073 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6074 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6076 DestroyWindow(hwnd);
6079 static void test_word_wrap(void)
6081 HWND hwnd;
6082 POINTL point = {0, 60}; /* This point must be below the first line */
6083 const char *text = "Must be long enough to test line wrapping";
6084 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6085 int res, pos, lines;
6087 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6088 * when specified on window creation and set later. */
6089 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6090 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6091 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6092 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6093 ok(res, "WM_SETTEXT failed.\n");
6094 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6095 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6096 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6097 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6099 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6100 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6101 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6102 DestroyWindow(hwnd);
6104 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6105 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6106 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6108 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6109 ok(res, "WM_SETTEXT failed.\n");
6110 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6111 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6112 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6113 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6115 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6116 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6117 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6118 DestroyWindow(hwnd);
6120 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6121 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6122 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6123 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6124 ok(res, "WM_SETTEXT failed.\n");
6125 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6126 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6128 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6129 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6130 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6131 DestroyWindow(hwnd);
6133 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6134 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6135 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6136 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6137 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6138 ok(res, "WM_SETTEXT failed.\n");
6139 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6140 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6142 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6143 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6144 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6146 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6147 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6148 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6149 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6150 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6152 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6153 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6154 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6155 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6156 DestroyWindow(hwnd);
6158 /* Test to see if wrapping happens with redraw disabled. */
6159 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6160 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6161 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6162 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6163 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6164 ok(res, "EM_REPLACESEL failed.\n");
6165 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6166 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6167 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6168 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6169 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6171 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6172 DestroyWindow(hwnd);
6175 static void test_autoscroll(void)
6177 HWND hwnd = new_richedit(NULL);
6178 int lines, ret, redraw;
6179 POINT pt;
6181 for (redraw = 0; redraw <= 1; redraw++) {
6182 trace("testing with WM_SETREDRAW=%d\n", redraw);
6183 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6184 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6185 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6186 ok(lines == 8, "%d lines instead of 8\n", lines);
6187 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6188 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6189 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6190 ret = GetWindowLong(hwnd, GWL_STYLE);
6191 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6193 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6194 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6195 ok(lines == 1, "%d lines instead of 1\n", lines);
6196 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6197 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6198 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6199 ret = GetWindowLong(hwnd, GWL_STYLE);
6200 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6203 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6204 DestroyWindow(hwnd);
6206 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6207 * auto vertical/horizontal scrolling options. */
6208 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6209 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6210 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6211 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6212 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6213 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6214 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6215 ret = GetWindowLong(hwnd, GWL_STYLE);
6216 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6217 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6218 DestroyWindow(hwnd);
6220 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6221 WS_POPUP|ES_MULTILINE,
6222 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6223 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6224 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6225 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6226 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6227 ret = GetWindowLong(hwnd, GWL_STYLE);
6228 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6229 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6230 DestroyWindow(hwnd);
6234 static void test_format_rect(void)
6236 HWND hwnd;
6237 RECT rc, expected, clientRect;
6238 int n;
6239 DWORD options;
6241 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6242 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6243 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6244 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6246 GetClientRect(hwnd, &clientRect);
6248 expected = clientRect;
6249 expected.left += 1;
6250 expected.right -= 1;
6251 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6252 ok(rc.top == expected.top && rc.left == expected.left &&
6253 rc.bottom == expected.bottom && rc.right == expected.right,
6254 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6255 rc.top, rc.left, rc.bottom, rc.right,
6256 expected.top, expected.left, expected.bottom, expected.right);
6258 for (n = -3; n <= 3; n++)
6260 rc = clientRect;
6261 rc.top += n;
6262 rc.left += n;
6263 rc.bottom -= n;
6264 rc.right -= n;
6265 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6267 expected = rc;
6268 expected.top = max(0, rc.top);
6269 expected.left = max(0, rc.left);
6270 expected.bottom = min(clientRect.bottom, rc.bottom);
6271 expected.right = min(clientRect.right, rc.right);
6272 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6273 ok(rc.top == expected.top && rc.left == expected.left &&
6274 rc.bottom == expected.bottom && rc.right == expected.right,
6275 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6276 n, rc.top, rc.left, rc.bottom, rc.right,
6277 expected.top, expected.left, expected.bottom, expected.right);
6280 rc = clientRect;
6281 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6282 expected = clientRect;
6283 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6284 ok(rc.top == expected.top && rc.left == expected.left &&
6285 rc.bottom == expected.bottom && rc.right == expected.right,
6286 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6287 rc.top, rc.left, rc.bottom, rc.right,
6288 expected.top, expected.left, expected.bottom, expected.right);
6290 /* Adding the selectionbar adds the selectionbar width to the left side. */
6291 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6292 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6293 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6294 expected.left += 8; /* selection bar width */
6295 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6296 ok(rc.top == expected.top && rc.left == expected.left &&
6297 rc.bottom == expected.bottom && rc.right == expected.right,
6298 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6299 rc.top, rc.left, rc.bottom, rc.right,
6300 expected.top, expected.left, expected.bottom, expected.right);
6302 rc = clientRect;
6303 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6304 expected = clientRect;
6305 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6306 ok(rc.top == expected.top && rc.left == expected.left &&
6307 rc.bottom == expected.bottom && rc.right == expected.right,
6308 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6309 rc.top, rc.left, rc.bottom, rc.right,
6310 expected.top, expected.left, expected.bottom, expected.right);
6312 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6313 * even if the left side is already 0. */
6314 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6315 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6316 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6317 expected.left -= 8; /* selection bar width */
6318 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6319 ok(rc.top == expected.top && rc.left == expected.left &&
6320 rc.bottom == expected.bottom && rc.right == expected.right,
6321 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6322 rc.top, rc.left, rc.bottom, rc.right,
6323 expected.top, expected.left, expected.bottom, expected.right);
6325 /* Set the absolute value of the formatting rectangle. */
6326 rc = clientRect;
6327 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6328 expected = clientRect;
6329 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6330 ok(rc.top == expected.top && rc.left == expected.left &&
6331 rc.bottom == expected.bottom && rc.right == expected.right,
6332 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6333 n, rc.top, rc.left, rc.bottom, rc.right,
6334 expected.top, expected.left, expected.bottom, expected.right);
6336 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6337 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6338 * tests show that this isn't true. */
6339 rc.top = 15;
6340 rc.left = 15;
6341 rc.bottom = clientRect.bottom - 15;
6342 rc.right = clientRect.right - 15;
6343 expected = rc;
6344 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6345 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6346 ok(rc.top == expected.top && rc.left == expected.left &&
6347 rc.bottom == expected.bottom && rc.right == expected.right,
6348 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6349 rc.top, rc.left, rc.bottom, rc.right,
6350 expected.top, expected.left, expected.bottom, expected.right);
6352 /* For some reason it does not limit the values to the client rect with
6353 * a WPARAM value of 1. */
6354 rc.top = -15;
6355 rc.left = -15;
6356 rc.bottom = clientRect.bottom + 15;
6357 rc.right = clientRect.right + 15;
6358 expected = rc;
6359 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6360 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6361 ok(rc.top == expected.top && rc.left == expected.left &&
6362 rc.bottom == expected.bottom && rc.right == expected.right,
6363 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6364 rc.top, rc.left, rc.bottom, rc.right,
6365 expected.top, expected.left, expected.bottom, expected.right);
6367 /* Reset to default rect and check how the format rect adjusts to window
6368 * resize and how it copes with very small windows */
6369 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)NULL);
6371 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6372 GetClientRect(hwnd, &clientRect);
6374 expected = clientRect;
6375 expected.left += 1;
6376 expected.right -= 1;
6377 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6378 ok(rc.top == expected.top && rc.left == expected.left &&
6379 rc.bottom == expected.bottom && rc.right == expected.right,
6380 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6381 rc.top, rc.left, rc.bottom, rc.right,
6382 expected.top, expected.left, expected.bottom, expected.right);
6384 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6385 GetClientRect(hwnd, &clientRect);
6387 expected = clientRect;
6388 expected.left += 1;
6389 expected.right -= 1;
6390 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6391 ok(rc.top == expected.top && rc.left == expected.left &&
6392 rc.bottom == expected.bottom && rc.right == expected.right,
6393 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6394 rc.top, rc.left, rc.bottom, rc.right,
6395 expected.top, expected.left, expected.bottom, expected.right);
6397 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6398 GetClientRect(hwnd, &clientRect);
6400 expected = clientRect;
6401 expected.left += 1;
6402 expected.right -= 1;
6403 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6404 ok(rc.top == expected.top && rc.left == expected.left &&
6405 rc.bottom == expected.bottom && rc.right == expected.right,
6406 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6407 rc.top, rc.left, rc.bottom, rc.right,
6408 expected.top, expected.left, expected.bottom, expected.right);
6410 DestroyWindow(hwnd);
6412 /* The extended window style affects the formatting rectangle. */
6413 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6414 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6415 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6416 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6418 GetClientRect(hwnd, &clientRect);
6420 expected = clientRect;
6421 expected.left += 1;
6422 expected.top += 1;
6423 expected.right -= 1;
6424 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6425 ok(rc.top == expected.top && rc.left == expected.left &&
6426 rc.bottom == expected.bottom && rc.right == expected.right,
6427 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6428 rc.top, rc.left, rc.bottom, rc.right,
6429 expected.top, expected.left, expected.bottom, expected.right);
6431 rc = clientRect;
6432 rc.top += 5;
6433 rc.left += 5;
6434 rc.bottom -= 5;
6435 rc.right -= 5;
6436 expected = rc;
6437 expected.top -= 1;
6438 expected.left -= 1;
6439 expected.right += 1;
6440 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6441 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6442 ok(rc.top == expected.top && rc.left == expected.left &&
6443 rc.bottom == expected.bottom && rc.right == expected.right,
6444 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6445 rc.top, rc.left, rc.bottom, rc.right,
6446 expected.top, expected.left, expected.bottom, expected.right);
6448 DestroyWindow(hwnd);
6451 static void test_WM_GETDLGCODE(void)
6453 HWND hwnd;
6454 UINT res, expected;
6455 MSG msg;
6457 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6459 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6460 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6461 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6462 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6463 msg.hwnd = hwnd;
6464 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6465 expected = expected | DLGC_WANTMESSAGE;
6466 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6467 res, expected);
6468 DestroyWindow(hwnd);
6470 msg.message = WM_KEYDOWN;
6471 msg.wParam = VK_RETURN;
6472 msg.lParam = (MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6473 msg.pt.x = 0;
6474 msg.pt.y = 0;
6475 msg.time = GetTickCount();
6477 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6478 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6479 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6480 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6481 msg.hwnd = hwnd;
6482 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6483 expected = expected | DLGC_WANTMESSAGE;
6484 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6485 res, expected);
6486 DestroyWindow(hwnd);
6488 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6489 ES_MULTILINE|WS_POPUP,
6490 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6491 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6492 msg.hwnd = hwnd;
6493 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6494 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6495 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6496 res, expected);
6497 DestroyWindow(hwnd);
6499 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6500 ES_WANTRETURN|WS_POPUP,
6501 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6502 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6503 msg.hwnd = hwnd;
6504 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6505 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6506 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6507 res, expected);
6508 DestroyWindow(hwnd);
6510 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6511 WS_POPUP,
6512 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6513 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6514 msg.hwnd = hwnd;
6515 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6516 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6517 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6518 res, expected);
6519 DestroyWindow(hwnd);
6521 msg.wParam = VK_TAB;
6522 msg.lParam = (MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6524 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6525 ES_MULTILINE|WS_POPUP,
6526 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6527 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6528 msg.hwnd = hwnd;
6529 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6530 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6531 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6532 res, expected);
6533 DestroyWindow(hwnd);
6535 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6536 WS_POPUP,
6537 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6538 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6539 msg.hwnd = hwnd;
6540 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6541 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6542 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6543 res, expected);
6544 DestroyWindow(hwnd);
6546 hold_key(VK_CONTROL);
6548 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6549 ES_MULTILINE|WS_POPUP,
6550 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6551 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6552 msg.hwnd = hwnd;
6553 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6554 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6555 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6556 res, expected);
6557 DestroyWindow(hwnd);
6559 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6560 WS_POPUP,
6561 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6562 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6563 msg.hwnd = hwnd;
6564 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6565 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6566 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6567 res, expected);
6568 DestroyWindow(hwnd);
6570 release_key(VK_CONTROL);
6572 msg.wParam = 'a';
6573 msg.lParam = (MapVirtualKey('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6575 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6576 ES_MULTILINE|WS_POPUP,
6577 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6578 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6579 msg.hwnd = hwnd;
6580 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6581 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6582 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6583 res, expected);
6584 DestroyWindow(hwnd);
6586 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6587 WS_POPUP,
6588 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6589 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6590 msg.hwnd = hwnd;
6591 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6592 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6593 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6594 res, expected);
6595 DestroyWindow(hwnd);
6597 msg.message = WM_CHAR;
6599 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6600 ES_MULTILINE|WS_POPUP,
6601 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6602 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6603 msg.hwnd = hwnd;
6604 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6605 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6606 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6607 res, expected);
6608 DestroyWindow(hwnd);
6610 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6611 WS_POPUP,
6612 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6613 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6614 msg.hwnd = hwnd;
6615 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6616 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6617 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6618 res, expected);
6619 DestroyWindow(hwnd);
6622 static void test_zoom(void)
6624 HWND hwnd;
6625 UINT ret;
6626 RECT rc;
6627 POINT pt;
6628 int numerator, denominator;
6630 hwnd = new_richedit(NULL);
6631 GetClientRect(hwnd, &rc);
6632 pt.x = (rc.right - rc.left) / 2;
6633 pt.y = (rc.bottom - rc.top) / 2;
6634 ClientToScreen(hwnd, &pt);
6636 /* Test initial zoom value */
6637 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6638 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6639 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6640 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6642 /* test scroll wheel */
6643 hold_key(VK_CONTROL);
6644 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6645 MAKELPARAM(pt.x, pt.y));
6646 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6647 release_key(VK_CONTROL);
6649 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6650 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6651 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6652 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6654 /* Test how much the mouse wheel can zoom in and out. */
6655 ret = SendMessage(hwnd, EM_SETZOOM, 490, 100);
6656 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6658 hold_key(VK_CONTROL);
6659 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6660 MAKELPARAM(pt.x, pt.y));
6661 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6662 release_key(VK_CONTROL);
6664 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6665 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6666 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6667 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6669 ret = SendMessage(hwnd, EM_SETZOOM, 491, 100);
6670 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6672 hold_key(VK_CONTROL);
6673 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6674 MAKELPARAM(pt.x, pt.y));
6675 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6676 release_key(VK_CONTROL);
6678 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6679 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6680 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6681 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6683 ret = SendMessage(hwnd, EM_SETZOOM, 20, 100);
6684 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6686 hold_key(VK_CONTROL);
6687 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6688 MAKELPARAM(pt.x, pt.y));
6689 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6690 release_key(VK_CONTROL);
6692 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6693 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6694 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6695 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6697 ret = SendMessage(hwnd, EM_SETZOOM, 19, 100);
6698 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6700 hold_key(VK_CONTROL);
6701 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6702 MAKELPARAM(pt.x, pt.y));
6703 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6704 release_key(VK_CONTROL);
6706 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6707 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6708 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6709 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6711 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6712 ret = SendMessage(hwnd, EM_SETZOOM, 50, 13);
6713 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6715 hold_key(VK_CONTROL);
6716 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6717 MAKELPARAM(pt.x, pt.y));
6718 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6719 release_key(VK_CONTROL);
6721 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6722 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6723 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6724 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6726 /* Test bounds checking on EM_SETZOOM */
6727 ret = SendMessage(hwnd, EM_SETZOOM, 2, 127);
6728 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6730 ret = SendMessage(hwnd, EM_SETZOOM, 127, 2);
6731 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6733 ret = SendMessage(hwnd, EM_SETZOOM, 2, 128);
6734 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6736 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6737 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6738 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6739 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6741 ret = SendMessage(hwnd, EM_SETZOOM, 128, 2);
6742 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6744 /* See if negative numbers are accepted. */
6745 ret = SendMessage(hwnd, EM_SETZOOM, -100, -100);
6746 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6748 /* See if negative numbers are accepted. */
6749 ret = SendMessage(hwnd, EM_SETZOOM, 0, 100);
6750 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6752 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6753 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6754 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6755 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6757 /* Reset the zoom value */
6758 ret = SendMessage(hwnd, EM_SETZOOM, 0, 0);
6759 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6761 DestroyWindow(hwnd);
6764 struct dialog_mode_messages
6766 int wm_getdefid, wm_close, wm_nextdlgctl;
6769 static struct dialog_mode_messages dm_messages;
6771 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6772 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6773 "got %d\n", wmclose, dm_messages.wm_close); \
6774 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6775 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6776 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6777 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6779 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6781 switch (iMsg)
6783 case DM_GETDEFID:
6784 dm_messages.wm_getdefid++;
6785 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6786 case WM_NEXTDLGCTL:
6787 dm_messages.wm_nextdlgctl++;
6788 break;
6789 case WM_CLOSE:
6790 dm_messages.wm_close++;
6791 break;
6794 return DefWindowProc(hwnd, iMsg, wParam, lParam);
6797 static void test_dialogmode(void)
6799 HWND hwRichEdit, hwParent, hwButton;
6800 MSG msg= {0};
6801 int lcount, r;
6802 WNDCLASSA cls;
6804 cls.style = 0;
6805 cls.lpfnWndProc = dialog_mode_wnd_proc;
6806 cls.cbClsExtra = 0;
6807 cls.cbWndExtra = 0;
6808 cls.hInstance = GetModuleHandleA(0);
6809 cls.hIcon = 0;
6810 cls.hCursor = LoadCursorA(0, IDC_ARROW);
6811 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6812 cls.lpszMenuName = NULL;
6813 cls.lpszClassName = "DialogModeParentClass";
6814 if(!RegisterClassA(&cls)) assert(0);
6816 hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6817 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6819 /* Test richedit(ES_MULTILINE) */
6821 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6823 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6824 ok(0 == r, "expected 0, got %d\n", r);
6825 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6826 ok(2 == lcount, "expected 2, got %d\n", lcount);
6828 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6829 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
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(3 == lcount, "expected 3, got %d\n", lcount);
6836 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6837 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6838 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6839 ok(0 == r, "expected 0, got %d\n", r);
6840 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6841 ok(3 == lcount, "expected 3, got %d\n", lcount);
6843 DestroyWindow(hwRichEdit);
6845 /* Test standalone richedit(ES_MULTILINE) */
6847 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6849 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6850 ok(0 == r, "expected 0, got %d\n", r);
6851 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6852 ok(2 == lcount, "expected 2, got %d\n", lcount);
6854 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6855 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
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 DestroyWindow(hwRichEdit);
6864 /* Check a destination for messages */
6866 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6868 SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6869 SetParent( hwRichEdit, NULL);
6871 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6872 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6874 memset(&dm_messages, 0, sizeof(dm_messages));
6875 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6876 ok(0 == r, "expected 0, got %d\n", r);
6877 test_dm_messages(0, 1, 0);
6879 memset(&dm_messages, 0, sizeof(dm_messages));
6880 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6881 ok(0 == r, "expected 0, got %d\n", r);
6882 test_dm_messages(0, 0, 1);
6884 DestroyWindow(hwRichEdit);
6886 /* Check messages from richedit(ES_MULTILINE) */
6888 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6890 memset(&dm_messages, 0, sizeof(dm_messages));
6891 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6892 ok(0 == r, "expected 0, got %d\n", r);
6893 test_dm_messages(0, 0, 0);
6895 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6896 ok(2 == lcount, "expected 2, got %d\n", lcount);
6898 memset(&dm_messages, 0, sizeof(dm_messages));
6899 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6900 ok(0 == r, "expected 0, got %d\n", r);
6901 test_dm_messages(0, 0, 0);
6903 memset(&dm_messages, 0, sizeof(dm_messages));
6904 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6905 ok(0 == r, "expected 0, got %d\n", r);
6906 test_dm_messages(0, 0, 0);
6908 memset(&dm_messages, 0, sizeof(dm_messages));
6909 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6910 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6911 test_dm_messages(0, 0, 0);
6913 memset(&dm_messages, 0, sizeof(dm_messages));
6914 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6915 ok(0 == r, "expected 0, got %d\n", r);
6916 test_dm_messages(0, 1, 0);
6918 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6919 ok(2 == lcount, "expected 2, got %d\n", lcount);
6921 memset(&dm_messages, 0, sizeof(dm_messages));
6922 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6923 ok(0 == r, "expected 0, got %d\n", r);
6924 test_dm_messages(0, 0, 0);
6926 memset(&dm_messages, 0, sizeof(dm_messages));
6927 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6928 ok(0 == r, "expected 0, got %d\n", r);
6929 test_dm_messages(0, 0, 1);
6931 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6932 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6933 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6935 memset(&dm_messages, 0, sizeof(dm_messages));
6936 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6937 ok(0 == r, "expected 0, got %d\n", r);
6938 test_dm_messages(0, 1, 1);
6940 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6941 ok(2 == lcount, "expected 2, got %d\n", lcount);
6943 DestroyWindow(hwButton);
6944 DestroyWindow(hwRichEdit);
6946 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
6948 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, hwParent);
6950 memset(&dm_messages, 0, sizeof(dm_messages));
6951 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6952 ok(0 == r, "expected 0, got %d\n", r);
6953 test_dm_messages(0, 0, 0);
6955 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6956 ok(2 == lcount, "expected 2, got %d\n", lcount);
6958 memset(&dm_messages, 0, sizeof(dm_messages));
6959 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6960 ok(0 == r, "expected 0, got %d\n", r);
6961 test_dm_messages(0, 0, 0);
6963 memset(&dm_messages, 0, sizeof(dm_messages));
6964 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6965 ok(0 == r, "expected 0, got %d\n", r);
6966 test_dm_messages(0, 0, 0);
6968 memset(&dm_messages, 0, sizeof(dm_messages));
6969 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6970 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6971 test_dm_messages(0, 0, 0);
6973 memset(&dm_messages, 0, sizeof(dm_messages));
6974 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6975 ok(0 == r, "expected 0, got %d\n", r);
6976 test_dm_messages(0, 0, 0);
6978 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6979 ok(3 == lcount, "expected 3, got %d\n", lcount);
6981 memset(&dm_messages, 0, sizeof(dm_messages));
6982 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6983 ok(0 == r, "expected 0, got %d\n", r);
6984 test_dm_messages(0, 0, 0);
6986 memset(&dm_messages, 0, sizeof(dm_messages));
6987 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6988 ok(0 == r, "expected 0, got %d\n", r);
6989 test_dm_messages(0, 0, 1);
6991 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6992 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6993 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6995 memset(&dm_messages, 0, sizeof(dm_messages));
6996 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6997 ok(0 == r, "expected 0, got %d\n", r);
6998 test_dm_messages(0, 0, 0);
7000 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7001 ok(4 == lcount, "expected 4, got %d\n", lcount);
7003 DestroyWindow(hwButton);
7004 DestroyWindow(hwRichEdit);
7006 /* Check messages from richedit(0) */
7008 hwRichEdit = new_window(RICHEDIT_CLASS, 0, hwParent);
7010 memset(&dm_messages, 0, sizeof(dm_messages));
7011 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7012 ok(0 == r, "expected 0, got %d\n", r);
7013 test_dm_messages(0, 0, 0);
7015 memset(&dm_messages, 0, sizeof(dm_messages));
7016 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7017 ok(0 == r, "expected 0, got %d\n", r);
7018 test_dm_messages(0, 0, 0);
7020 memset(&dm_messages, 0, sizeof(dm_messages));
7021 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7022 ok(0 == r, "expected 0, got %d\n", r);
7023 test_dm_messages(0, 0, 0);
7025 memset(&dm_messages, 0, sizeof(dm_messages));
7026 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7027 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7028 test_dm_messages(0, 0, 0);
7030 memset(&dm_messages, 0, sizeof(dm_messages));
7031 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7032 ok(0 == r, "expected 0, got %d\n", r);
7033 test_dm_messages(0, 1, 0);
7035 memset(&dm_messages, 0, sizeof(dm_messages));
7036 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7037 ok(0 == r, "expected 0, got %d\n", r);
7038 test_dm_messages(0, 0, 0);
7040 memset(&dm_messages, 0, sizeof(dm_messages));
7041 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7042 ok(0 == r, "expected 0, got %d\n", r);
7043 test_dm_messages(0, 0, 1);
7045 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7046 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7047 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7049 memset(&dm_messages, 0, sizeof(dm_messages));
7050 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7051 ok(0 == r, "expected 0, got %d\n", r);
7052 test_dm_messages(0, 1, 1);
7054 DestroyWindow(hwRichEdit);
7056 /* Check messages from richedit(ES_WANTRETURN) */
7058 hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7060 memset(&dm_messages, 0, sizeof(dm_messages));
7061 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7062 ok(0 == r, "expected 0, got %d\n", r);
7063 test_dm_messages(0, 0, 0);
7065 memset(&dm_messages, 0, sizeof(dm_messages));
7066 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7067 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7068 test_dm_messages(0, 0, 0);
7070 memset(&dm_messages, 0, sizeof(dm_messages));
7071 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7072 ok(0 == r, "expected 0, got %d\n", r);
7073 test_dm_messages(0, 0, 0);
7075 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7076 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7077 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7079 memset(&dm_messages, 0, sizeof(dm_messages));
7080 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7081 ok(0 == r, "expected 0, got %d\n", r);
7082 test_dm_messages(0, 0, 0);
7084 DestroyWindow(hwRichEdit);
7085 DestroyWindow(hwParent);
7088 static void test_EM_FINDWORDBREAK_W(void)
7090 static const struct {
7091 WCHAR c;
7092 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7093 } delimiter_tests[] = {
7094 {0x0a, FALSE}, /* newline */
7095 {0x0b, FALSE}, /* vertical tab */
7096 {0x0c, FALSE}, /* form feed */
7097 {0x0d, FALSE}, /* carriage return */
7098 {0x20, TRUE}, /* space */
7099 {0x61, FALSE}, /* capital letter a */
7100 {0xa0, FALSE}, /* no-break space */
7101 {0x2000, FALSE}, /* en quad */
7102 {0x3000, FALSE}, /* Ideographic space */
7103 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7104 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7105 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7106 {0xac00, FALSE}, /* Hangul character GA*/
7107 {0xd7af, FALSE}, /* End of Hangul character chart */
7108 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7109 {0xff20, FALSE}, /* fullwidth commercial @ */
7110 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7112 int i;
7113 HWND hwndRichEdit = new_richeditW(NULL);
7114 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7115 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7117 WCHAR wbuf[2];
7118 int result;
7120 wbuf[0] = delimiter_tests[i].c;
7121 wbuf[1] = 0;
7122 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7123 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7124 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7125 todo_wine
7126 ok(result == delimiter_tests[i].isdelimiter,
7127 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7128 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7129 else
7130 ok(result == delimiter_tests[i].isdelimiter,
7131 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7132 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7134 DestroyWindow(hwndRichEdit);
7137 static void test_EM_FINDWORDBREAK_A(void)
7139 static const struct {
7140 WCHAR c;
7141 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7142 } delimiter_tests[] = {
7143 {0x0a, FALSE}, /* newline */
7144 {0x0b, FALSE}, /* vertical tab */
7145 {0x0c, FALSE}, /* form feed */
7146 {0x0d, FALSE}, /* carriage return */
7147 {0x20, TRUE}, /* space */
7148 {0x61, FALSE}, /* capital letter a */
7150 int i;
7151 HWND hwndRichEdit = new_richedit(NULL);
7153 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7154 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7156 int result;
7157 char buf[2];
7158 buf[0] = delimiter_tests[i].c;
7159 buf[1] = 0;
7160 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7161 result = SendMessage(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7162 if (buf[0] == 0x20)
7163 todo_wine
7164 ok(result == delimiter_tests[i].isdelimiter,
7165 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7166 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7167 else
7168 ok(result == delimiter_tests[i].isdelimiter,
7169 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7170 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7172 DestroyWindow(hwndRichEdit);
7175 START_TEST( editor )
7177 BOOL ret;
7178 /* Must explicitly LoadLibrary(). The test has no references to functions in
7179 * RICHED20.DLL, so the linker doesn't actually link to it. */
7180 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
7181 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7183 test_WM_CHAR();
7184 test_EM_FINDTEXT();
7185 test_EM_GETLINE();
7186 test_EM_POSFROMCHAR();
7187 test_EM_SCROLLCARET();
7188 test_EM_SCROLL();
7189 test_scrollbar_visibility();
7190 test_WM_SETTEXT();
7191 test_EM_LINELENGTH();
7192 test_EM_SETCHARFORMAT();
7193 test_EM_SETTEXTMODE();
7194 test_TM_PLAINTEXT();
7195 test_EM_SETOPTIONS();
7196 test_WM_GETTEXT();
7197 test_EM_GETTEXTRANGE();
7198 test_EM_GETSELTEXT();
7199 test_EM_SETUNDOLIMIT();
7200 test_ES_PASSWORD();
7201 test_EM_SETTEXTEX();
7202 test_EM_LIMITTEXT();
7203 test_EM_EXLIMITTEXT();
7204 test_EM_GETLIMITTEXT();
7205 test_WM_SETFONT();
7206 test_EM_GETMODIFY();
7207 test_EM_EXSETSEL();
7208 test_WM_PASTE();
7209 test_EM_STREAMIN();
7210 test_EM_STREAMOUT();
7211 test_EM_STREAMOUT_FONTTBL();
7212 test_EM_StreamIn_Undo();
7213 test_EM_FORMATRANGE();
7214 test_unicode_conversions();
7215 test_EM_GETTEXTLENGTHEX();
7216 test_EM_REPLACESEL(1);
7217 test_EM_REPLACESEL(0);
7218 test_WM_NOTIFY();
7219 test_EM_AUTOURLDETECT();
7220 test_eventMask();
7221 test_undo_coalescing();
7222 test_word_movement();
7223 test_EM_CHARFROMPOS();
7224 test_SETPARAFORMAT();
7225 test_word_wrap();
7226 test_autoscroll();
7227 test_format_rect();
7228 test_WM_GETDLGCODE();
7229 test_zoom();
7230 test_dialogmode();
7231 test_EM_FINDWORDBREAK_W();
7232 test_EM_FINDWORDBREAK_A();
7234 /* Set the environment variable WINETEST_RICHED20 to keep windows
7235 * responsive and open for 30 seconds. This is useful for debugging.
7237 if (getenv( "WINETEST_RICHED20" )) {
7238 keep_responsive(30);
7241 OleFlushClipboard();
7242 ret = FreeLibrary(hmoduleRichEdit);
7243 ok(ret, "error: %d\n", (int) GetLastError());