push 1a3d31f62adec93e6e6d221f242eb86df2604322
[wine/hacks.git] / dlls / riched20 / tests / editor.c
blob70c100ae5d3b939a6535c0a8ddc164241569cc0d
1 /*
2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
36 #define ID_RICHEDITTESTDBUTTON 0x123
38 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
40 #define ok_w3(format, szString1, szString2, szString3) \
41 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
42 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
43 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
44 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
45 format, string1, string2, string3);
47 static HMODULE hmoduleRichEdit;
49 static int is_win9x = 0;
51 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
52 HWND hwnd;
53 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
54 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
55 hmoduleRichEdit, NULL);
56 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
57 return hwnd;
60 static HWND new_richedit(HWND parent) {
61 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
64 /* Keeps the window reponsive for the deley_time in seconds.
65 * This is useful for debugging a test to see what is happening. */
66 static void keep_responsive(time_t delay_time)
68 MSG msg;
69 time_t end;
71 /* The message pump uses PeekMessage() to empty the queue and then
72 * sleeps for 50ms before retrying the queue. */
73 end = time(NULL) + delay_time;
74 while (time(NULL) < end) {
75 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
76 TranslateMessage(&msg);
77 DispatchMessage(&msg);
78 } else {
79 Sleep(50);
84 static void processPendingMessages(void)
86 MSG msg;
87 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
88 TranslateMessage(&msg);
89 DispatchMessage(&msg);
93 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
95 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
96 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
97 SetFocus(hwnd);
98 keybd_event(mod_vk, mod_scan_code, 0, 0);
99 keybd_event(vk, scan_code, 0, 0);
100 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
101 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
102 processPendingMessages();
105 static void simulate_typing_characters(HWND hwnd, const char* szChars)
107 int ret;
109 while (*szChars != '\0') {
110 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
111 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
112 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
113 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
114 szChars++;
118 static BOOL hold_key(int vk)
120 BYTE key_state[256];
121 BOOL result;
123 result = GetKeyboardState(key_state);
124 ok(result, "GetKeyboardState failed.\n");
125 if (!result) return FALSE;
126 key_state[vk] |= 0x80;
127 result = SetKeyboardState(key_state);
128 ok(result, "SetKeyboardState failed.\n");
129 return result != 0;
132 static BOOL release_key(int vk)
134 BYTE key_state[256];
135 BOOL result;
137 result = GetKeyboardState(key_state);
138 ok(result, "GetKeyboardState failed.\n");
139 if (!result) return FALSE;
140 key_state[vk] &= ~0x80;
141 result = SetKeyboardState(key_state);
142 ok(result, "SetKeyboardState failed.\n");
143 return result != 0;
146 static const char haystack[] = "WINEWine wineWine wine WineWine";
147 /* ^0 ^10 ^20 ^30 */
149 struct find_s {
150 int start;
151 int end;
152 const char *needle;
153 int flags;
154 int expected_loc;
158 struct find_s find_tests[] = {
159 /* Find in empty text */
160 {0, -1, "foo", FR_DOWN, -1},
161 {0, -1, "foo", 0, -1},
162 {0, -1, "", FR_DOWN, -1},
163 {20, 5, "foo", FR_DOWN, -1},
164 {5, 20, "foo", FR_DOWN, -1}
167 struct find_s find_tests2[] = {
168 /* No-result find */
169 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
170 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
172 /* Subsequent finds */
173 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
174 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
175 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
176 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
178 /* Find backwards */
179 {19, 20, "Wine", FR_MATCHCASE, 13},
180 {10, 20, "Wine", FR_MATCHCASE, 4},
181 {20, 10, "Wine", FR_MATCHCASE, 13},
183 /* Case-insensitive */
184 {1, 31, "wInE", FR_DOWN, 4},
185 {1, 31, "Wine", FR_DOWN, 4},
187 /* High-to-low ranges */
188 {20, 5, "Wine", FR_DOWN, -1},
189 {2, 1, "Wine", FR_DOWN, -1},
190 {30, 29, "Wine", FR_DOWN, -1},
191 {20, 5, "Wine", 0, 13},
193 /* Find nothing */
194 {5, 10, "", FR_DOWN, -1},
195 {10, 5, "", FR_DOWN, -1},
196 {0, -1, "", FR_DOWN, -1},
197 {10, 5, "", 0, -1},
199 /* Whole-word search */
200 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
201 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
202 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
203 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
204 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
205 {11, -1, "winewine", FR_WHOLEWORD, 0},
206 {31, -1, "winewine", FR_WHOLEWORD, 23},
208 /* Bad ranges */
209 {5, 200, "XXX", FR_DOWN, -1},
210 {-20, 20, "Wine", FR_DOWN, -1},
211 {-20, 20, "Wine", FR_DOWN, -1},
212 {-15, -20, "Wine", FR_DOWN, -1},
213 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
215 /* Check the case noted in bug 4479 where matches at end aren't recognized */
216 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
217 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
218 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
219 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
220 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
222 /* The backwards case of bug 4479; bounds look right
223 * Fails because backward find is wrong */
224 {19, 20, "WINE", FR_MATCHCASE, 0},
225 {0, 20, "WINE", FR_MATCHCASE, -1},
227 {0, -1, "wineWine wine", 0, -1},
230 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
231 int findloc;
232 FINDTEXT ft;
233 memset(&ft, 0, sizeof(ft));
234 ft.chrg.cpMin = f->start;
235 ft.chrg.cpMax = f->end;
236 ft.lpstrText = f->needle;
237 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
238 ok(findloc == f->expected_loc,
239 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
240 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
243 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
244 int id) {
245 int findloc;
246 FINDTEXTEX ft;
247 int expected_end_loc;
249 memset(&ft, 0, sizeof(ft));
250 ft.chrg.cpMin = f->start;
251 ft.chrg.cpMax = f->end;
252 ft.lpstrText = f->needle;
253 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
254 ok(findloc == f->expected_loc,
255 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
256 name, id, f->needle, f->start, f->end, f->flags, findloc);
257 ok(ft.chrgText.cpMin == f->expected_loc,
258 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
259 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
260 expected_end_loc = ((f->expected_loc == -1) ? -1
261 : f->expected_loc + strlen(f->needle));
262 ok(ft.chrgText.cpMax == expected_end_loc,
263 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
264 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
267 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
268 int num_tests)
270 int i;
272 for (i = 0; i < num_tests; i++) {
273 check_EM_FINDTEXT(hwnd, name, &find[i], i);
274 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
278 static void test_EM_FINDTEXT(void)
280 HWND hwndRichEdit = new_richedit(NULL);
281 CHARFORMAT2 cf2;
283 /* Empty rich edit control */
284 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
285 sizeof(find_tests)/sizeof(struct find_s));
287 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
289 /* Haystack text */
290 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
291 sizeof(find_tests2)/sizeof(struct find_s));
293 /* Setting a format on an arbitrary range should have no effect in search
294 results. This tests correct offset reporting across runs. */
295 cf2.cbSize = sizeof(CHARFORMAT2);
296 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
297 (LPARAM) &cf2);
298 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
299 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
300 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
301 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
303 /* Haystack text, again */
304 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
305 sizeof(find_tests2)/sizeof(struct find_s));
307 /* Yet another range */
308 cf2.dwMask = CFM_BOLD | cf2.dwMask;
309 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
310 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
311 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
313 /* Haystack text, again */
314 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
315 sizeof(find_tests2)/sizeof(struct find_s));
317 DestroyWindow(hwndRichEdit);
320 static const struct getline_s {
321 int line;
322 size_t buffer_len;
323 const char *text;
324 } gl[] = {
325 {0, 10, "foo bar\r"},
326 {1, 10, "\r"},
327 {2, 10, "bar\r"},
328 {3, 10, "\r"},
330 /* Buffer smaller than line length */
331 {0, 2, "foo bar\r"},
332 {0, 1, "foo bar\r"},
333 {0, 0, "foo bar\r"}
336 static void test_EM_GETLINE(void)
338 int i;
339 HWND hwndRichEdit = new_richedit(NULL);
340 static const int nBuf = 1024;
341 char dest[1024], origdest[1024];
342 const char text[] = "foo bar\n"
343 "\n"
344 "bar\n";
346 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
348 memset(origdest, 0xBB, nBuf);
349 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
351 int nCopied;
352 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
353 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
354 memset(dest, 0xBB, nBuf);
355 *(WORD *) dest = gl[i].buffer_len;
357 /* EM_GETLINE appends a "\r\0" to the end of the line
358 * nCopied counts up to and including the '\r' */
359 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
360 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
361 expected_nCopied);
362 /* two special cases since a parameter is passed via dest */
363 if (gl[i].buffer_len == 0)
364 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
365 "buffer_len=0\n");
366 else if (gl[i].buffer_len == 1)
367 ok(dest[0] == gl[i].text[0] && !dest[1] &&
368 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
369 else
371 /* Prepare hex strings of buffers to dump on failure. */
372 char expectedbuf[1024];
373 char resultbuf[1024];
374 int j;
375 resultbuf[0] = '\0';
376 for (j = 0; j < 32; j++)
377 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
378 expectedbuf[0] = '\0';
379 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
380 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
381 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
382 sprintf(expectedbuf+strlen(expectedbuf), "??");
383 for (; j < 32; j++) /* Bytes after declared buffer size */
384 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
386 /* Test the part of the buffer that is expected to be written according
387 * to the MSDN documentation fo EM_GETLINE, which does not state that
388 * a NULL terminating character will be added unless no text is copied.
390 * Windows 95, 98 & NT do not append a NULL terminating character, but
391 * Windows 2000 and up do append a NULL terminating character if there
392 * is space in the buffer. The test will ignore this difference. */
393 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
394 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
395 i, expected_bytes_written, expectedbuf, resultbuf);
396 /* Test the part of the buffer after the declared length to make sure
397 * there are no buffer overruns. */
398 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
399 nBuf - gl[i].buffer_len),
400 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
401 i, expected_bytes_written, expectedbuf, resultbuf);
405 DestroyWindow(hwndRichEdit);
408 static void test_EM_LINELENGTH(void)
410 HWND hwndRichEdit = new_richedit(NULL);
411 const char * text =
412 "richedit1\r"
413 "richedit1\n"
414 "richedit1\r\n"
415 "richedit1";
416 int offset_test[10][2] = {
417 {0, 9},
418 {5, 9},
419 {10, 9},
420 {15, 9},
421 {20, 9},
422 {25, 9},
423 {30, 9},
424 {35, 9},
425 {40, 0},
426 {45, 0},
428 int i;
429 LRESULT result;
431 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
433 for (i = 0; i < 10; i++) {
434 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
435 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
436 offset_test[i][0], result, offset_test[i][1]);
439 DestroyWindow(hwndRichEdit);
442 static int get_scroll_pos_y(HWND hwnd)
444 POINT p = {-1, -1};
445 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
446 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
447 return p.y;
450 static void move_cursor(HWND hwnd, LONG charindex)
452 CHARRANGE cr;
453 cr.cpMax = charindex;
454 cr.cpMin = charindex;
455 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
458 static void line_scroll(HWND hwnd, int amount)
460 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
463 static void test_EM_SCROLLCARET(void)
465 int prevY, curY;
466 const char text[] = "aa\n"
467 "this is a long line of text that should be longer than the "
468 "control's width\n"
469 "cc\n"
470 "dd\n"
471 "ee\n"
472 "ff\n"
473 "gg\n"
474 "hh\n";
475 /* The richedit window height needs to be large enough vertically to fit in
476 * more than two lines of text, so the new_richedit function can't be used
477 * since a height of 60 was not large enough on some systems.
479 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
480 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
481 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
482 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
484 /* Can't verify this */
485 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
487 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
489 /* Caret above visible window */
490 line_scroll(hwndRichEdit, 3);
491 prevY = get_scroll_pos_y(hwndRichEdit);
492 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
493 curY = get_scroll_pos_y(hwndRichEdit);
494 ok(prevY != curY, "%d == %d\n", prevY, curY);
496 /* Caret below visible window */
497 move_cursor(hwndRichEdit, sizeof(text) - 1);
498 line_scroll(hwndRichEdit, -3);
499 prevY = get_scroll_pos_y(hwndRichEdit);
500 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
501 curY = get_scroll_pos_y(hwndRichEdit);
502 ok(prevY != curY, "%d == %d\n", prevY, curY);
504 /* Caret in visible window */
505 move_cursor(hwndRichEdit, sizeof(text) - 2);
506 prevY = get_scroll_pos_y(hwndRichEdit);
507 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
508 curY = get_scroll_pos_y(hwndRichEdit);
509 ok(prevY == curY, "%d != %d\n", prevY, curY);
511 /* Caret still in visible window */
512 line_scroll(hwndRichEdit, -1);
513 prevY = get_scroll_pos_y(hwndRichEdit);
514 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
515 curY = get_scroll_pos_y(hwndRichEdit);
516 ok(prevY == curY, "%d != %d\n", prevY, curY);
518 DestroyWindow(hwndRichEdit);
521 static void test_EM_POSFROMCHAR(void)
523 HWND hwndRichEdit = new_richedit(NULL);
524 int i;
525 LRESULT result;
526 unsigned int height = 0;
527 int xpos = 0;
528 POINTL pt;
529 static const char text[] = "aa\n"
530 "this is a long line of text that should be longer than the "
531 "control's width\n"
532 "cc\n"
533 "dd\n"
534 "ee\n"
535 "ff\n"
536 "gg\n"
537 "hh\n";
539 /* Fill the control to lines to ensure that most of them are offscreen */
540 for (i = 0; i < 50; i++)
542 /* Do not modify the string; it is exactly 16 characters long. */
543 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
544 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
548 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
549 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
550 Richedit 3.0 accepts either of the above API conventions.
553 /* Testing Richedit 2.0 API format */
555 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
556 Since all lines are identical and drawn with the same font,
557 they should have the same height... right?
559 for (i = 0; i < 50; i++)
561 /* All the lines are 16 characters long */
562 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
563 if (i == 0)
565 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
566 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
567 xpos = LOWORD(result);
569 else if (i == 1)
571 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
572 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
573 height = HIWORD(result);
575 else
577 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
578 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
582 /* Testing position at end of text */
583 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
584 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
585 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
587 /* Testing position way past end of text */
588 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
589 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
590 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
592 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
593 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
594 for (i = 0; i < 50; i++)
596 /* All the lines are 16 characters long */
597 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
598 ok((signed short)(HIWORD(result)) == (i - 1) * height,
599 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
600 (signed short)(HIWORD(result)), (i - 1) * height);
601 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
604 /* Testing position at end of text */
605 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
606 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
607 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
609 /* Testing position way past end of text */
610 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
611 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
612 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
614 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
615 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
616 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
618 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
619 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
620 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
621 xpos = LOWORD(result);
623 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
624 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
625 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
626 ok((signed short)(LOWORD(result)) < xpos,
627 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
628 (signed short)(LOWORD(result)), xpos);
629 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
631 /* Test around end of text that doesn't end in a newline. */
632 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
633 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
634 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
635 ok(pt.x > 1, "pt.x = %d\n", pt.x);
636 xpos = pt.x;
637 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
638 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
639 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
640 xpos = pt.x;
641 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
642 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
643 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
645 /* Try a negative position. */
646 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
647 ok(pt.x == 1, "pt.x = %d\n", pt.x);
649 DestroyWindow(hwndRichEdit);
652 static void test_EM_SETCHARFORMAT(void)
654 HWND hwndRichEdit = new_richedit(NULL);
655 CHARFORMAT2 cf2;
656 int rc = 0;
657 int tested_effects[] = {
658 CFE_BOLD,
659 CFE_ITALIC,
660 CFE_UNDERLINE,
661 CFE_STRIKEOUT,
662 CFE_PROTECTED,
663 CFE_LINK,
664 CFE_SUBSCRIPT,
665 CFE_SUPERSCRIPT,
668 int i;
669 CHARRANGE cr;
671 /* Invalid flags, CHARFORMAT2 structure blanked out */
672 memset(&cf2, 0, sizeof(cf2));
673 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
674 (LPARAM) &cf2);
675 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
677 /* A valid flag, CHARFORMAT2 structure blanked out */
678 memset(&cf2, 0, sizeof(cf2));
679 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
680 (LPARAM) &cf2);
681 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
683 /* A valid flag, CHARFORMAT2 structure blanked out */
684 memset(&cf2, 0, sizeof(cf2));
685 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
686 (LPARAM) &cf2);
687 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
689 /* A valid flag, CHARFORMAT2 structure blanked out */
690 memset(&cf2, 0, sizeof(cf2));
691 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
692 (LPARAM) &cf2);
693 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
695 /* A valid flag, CHARFORMAT2 structure blanked out */
696 memset(&cf2, 0, sizeof(cf2));
697 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
698 (LPARAM) &cf2);
699 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
701 /* Invalid flags, CHARFORMAT2 structure minimally filled */
702 memset(&cf2, 0, sizeof(cf2));
703 cf2.cbSize = sizeof(CHARFORMAT2);
704 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
705 (LPARAM) &cf2);
706 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
707 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
708 ok(rc == FALSE, "Should not be able to undo here.\n");
709 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
711 /* A valid flag, CHARFORMAT2 structure minimally filled */
712 memset(&cf2, 0, sizeof(cf2));
713 cf2.cbSize = sizeof(CHARFORMAT2);
714 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
715 (LPARAM) &cf2);
716 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
717 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
718 ok(rc == FALSE, "Should not be able to undo here.\n");
719 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
721 /* A valid flag, CHARFORMAT2 structure minimally filled */
722 memset(&cf2, 0, sizeof(cf2));
723 cf2.cbSize = sizeof(CHARFORMAT2);
724 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
725 (LPARAM) &cf2);
726 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
727 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
728 ok(rc == FALSE, "Should not be able to undo here.\n");
729 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
731 /* A valid flag, CHARFORMAT2 structure minimally filled */
732 memset(&cf2, 0, sizeof(cf2));
733 cf2.cbSize = sizeof(CHARFORMAT2);
734 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
735 (LPARAM) &cf2);
736 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
737 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
738 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
739 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
741 /* A valid flag, CHARFORMAT2 structure minimally filled */
742 memset(&cf2, 0, sizeof(cf2));
743 cf2.cbSize = sizeof(CHARFORMAT2);
744 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
745 (LPARAM) &cf2);
746 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
747 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
748 ok(rc == TRUE, "Should not be able to undo here.\n");
749 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
751 cf2.cbSize = sizeof(CHARFORMAT2);
752 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
753 (LPARAM) &cf2);
755 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
756 cf2.cbSize = sizeof(CHARFORMAT2);
757 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
758 (LPARAM) &cf2);
759 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
760 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
762 /* wParam==0 is default char format, does not set modify */
763 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
764 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
765 ok(rc == 0, "Text marked as modified, expected not modified!\n");
766 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
767 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
768 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
769 ok(rc == 0, "Text marked as modified, expected not modified!\n");
771 /* wParam==SCF_SELECTION sets modify if nonempty selection */
772 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
773 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
774 ok(rc == 0, "Text marked as modified, expected not modified!\n");
775 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
776 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
777 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
778 ok(rc == 0, "Text marked as modified, expected not modified!\n");
780 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
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");
787 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
788 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
789 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
790 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
791 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
793 /* wParam==SCF_ALL sets modify regardless of whether text is present */
794 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
795 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
796 ok(rc == 0, "Text marked as modified, expected not modified!\n");
797 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
798 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
799 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
800 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
802 DestroyWindow(hwndRichEdit);
804 /* EM_GETCHARFORMAT tests */
805 for (i = 0; tested_effects[i]; i++)
807 hwndRichEdit = new_richedit(NULL);
808 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
810 /* Need to set a TrueType font to get consistent CFM_BOLD results */
811 memset(&cf2, 0, sizeof(CHARFORMAT2));
812 cf2.cbSize = sizeof(CHARFORMAT2);
813 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
814 cf2.dwEffects = 0;
815 strcpy(cf2.szFaceName, "Courier New");
816 cf2.wWeight = FW_DONTCARE;
817 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
819 memset(&cf2, 0, sizeof(CHARFORMAT2));
820 cf2.cbSize = sizeof(CHARFORMAT2);
821 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
822 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
823 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
824 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
826 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
827 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
828 ok((cf2.dwEffects & tested_effects[i]) == 0,
829 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
831 memset(&cf2, 0, sizeof(CHARFORMAT2));
832 cf2.cbSize = sizeof(CHARFORMAT2);
833 cf2.dwMask = tested_effects[i];
834 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
835 cf2.dwMask = CFM_SUPERSCRIPT;
836 cf2.dwEffects = tested_effects[i];
837 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
838 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
840 memset(&cf2, 0, sizeof(CHARFORMAT2));
841 cf2.cbSize = sizeof(CHARFORMAT2);
842 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
843 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
844 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
845 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
847 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
848 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
849 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
850 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
852 memset(&cf2, 0, sizeof(CHARFORMAT2));
853 cf2.cbSize = sizeof(CHARFORMAT2);
854 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
855 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
856 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
857 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
859 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
860 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
861 ok((cf2.dwEffects & tested_effects[i]) == 0,
862 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
864 memset(&cf2, 0, sizeof(CHARFORMAT2));
865 cf2.cbSize = sizeof(CHARFORMAT2);
866 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
867 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
868 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
869 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
871 (cf2.dwMask & tested_effects[i]) == 0),
872 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
874 DestroyWindow(hwndRichEdit);
877 for (i = 0; tested_effects[i]; i++)
879 hwndRichEdit = new_richedit(NULL);
880 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
882 /* Need to set a TrueType font to get consistent CFM_BOLD results */
883 memset(&cf2, 0, sizeof(CHARFORMAT2));
884 cf2.cbSize = sizeof(CHARFORMAT2);
885 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
886 cf2.dwEffects = 0;
887 strcpy(cf2.szFaceName, "Courier New");
888 cf2.wWeight = FW_DONTCARE;
889 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
891 memset(&cf2, 0, sizeof(CHARFORMAT2));
892 cf2.cbSize = sizeof(CHARFORMAT2);
893 cf2.dwMask = tested_effects[i];
894 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
895 cf2.dwMask = CFM_SUPERSCRIPT;
896 cf2.dwEffects = tested_effects[i];
897 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
898 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
900 memset(&cf2, 0, sizeof(CHARFORMAT2));
901 cf2.cbSize = sizeof(CHARFORMAT2);
902 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
903 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
904 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
905 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
907 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
908 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
909 ok((cf2.dwEffects & tested_effects[i]) == 0,
910 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
912 memset(&cf2, 0, sizeof(CHARFORMAT2));
913 cf2.cbSize = sizeof(CHARFORMAT2);
914 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
915 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
916 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
917 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
919 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
920 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
921 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
922 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
924 memset(&cf2, 0, sizeof(CHARFORMAT2));
925 cf2.cbSize = sizeof(CHARFORMAT2);
926 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
927 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
928 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
929 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
931 (cf2.dwMask & tested_effects[i]) == 0),
932 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
933 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
934 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
936 DestroyWindow(hwndRichEdit);
939 /* Effects applied on an empty selection should take effect when selection is
940 replaced with text */
941 hwndRichEdit = new_richedit(NULL);
942 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
943 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
945 memset(&cf2, 0, sizeof(CHARFORMAT2));
946 cf2.cbSize = sizeof(CHARFORMAT2);
947 cf2.dwMask = CFM_BOLD;
948 cf2.dwEffects = CFE_BOLD;
949 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
951 /* Selection is now nonempty */
952 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
954 memset(&cf2, 0, sizeof(CHARFORMAT2));
955 cf2.cbSize = sizeof(CHARFORMAT2);
956 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
957 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
959 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
960 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
961 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
962 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
965 /* Set two effects on an empty selection */
966 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
967 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
969 memset(&cf2, 0, sizeof(CHARFORMAT2));
970 cf2.cbSize = sizeof(CHARFORMAT2);
971 cf2.dwMask = CFM_BOLD;
972 cf2.dwEffects = CFE_BOLD;
973 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
974 cf2.dwMask = CFM_ITALIC;
975 cf2.dwEffects = CFE_ITALIC;
976 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
978 /* Selection is now nonempty */
979 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
981 memset(&cf2, 0, sizeof(CHARFORMAT2));
982 cf2.cbSize = sizeof(CHARFORMAT2);
983 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
984 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
986 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
987 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
988 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
989 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
991 /* Setting the (empty) selection to exactly the same place as before should
992 NOT clear the insertion style! */
993 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
994 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
996 memset(&cf2, 0, sizeof(CHARFORMAT2));
997 cf2.cbSize = sizeof(CHARFORMAT2);
998 cf2.dwMask = CFM_BOLD;
999 cf2.dwEffects = CFE_BOLD;
1000 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1002 /* Empty selection in same place, insert style should NOT be forgotten here. */
1003 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1005 /* Selection is now nonempty */
1006 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1008 memset(&cf2, 0, sizeof(CHARFORMAT2));
1009 cf2.cbSize = sizeof(CHARFORMAT2);
1010 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1011 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1013 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1014 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1015 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1016 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1018 /* Ditto with EM_EXSETSEL */
1019 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1020 cr.cpMin = 2; cr.cpMax = 2;
1021 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1023 memset(&cf2, 0, sizeof(CHARFORMAT2));
1024 cf2.cbSize = sizeof(CHARFORMAT2);
1025 cf2.dwMask = CFM_BOLD;
1026 cf2.dwEffects = CFE_BOLD;
1027 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1029 /* Empty selection in same place, insert style should NOT be forgotten here. */
1030 cr.cpMin = 2; cr.cpMax = 2;
1031 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1033 /* Selection is now nonempty */
1034 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1036 memset(&cf2, 0, sizeof(CHARFORMAT2));
1037 cf2.cbSize = sizeof(CHARFORMAT2);
1038 cr.cpMin = 2; cr.cpMax = 6;
1039 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1040 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1042 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1043 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1044 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1045 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1047 DestroyWindow(hwndRichEdit);
1050 static void test_EM_SETTEXTMODE(void)
1052 HWND hwndRichEdit = new_richedit(NULL);
1053 CHARFORMAT2 cf2, cf2test;
1054 CHARRANGE cr;
1055 int rc = 0;
1057 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1058 /*Insert text into the control*/
1060 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1062 /*Attempt to change the control to plain text mode*/
1063 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1064 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1066 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1067 If rich text is pasted, it should have the same formatting as the rest
1068 of the text in the control*/
1070 /*Italicize the text
1071 *NOTE: If the default text was already italicized, the test will simply
1072 reverse; in other words, it will copy a regular "wine" into a plain
1073 text window that uses an italicized format*/
1074 cf2.cbSize = sizeof(CHARFORMAT2);
1075 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1076 (LPARAM) &cf2);
1078 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1079 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1081 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1082 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1084 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1085 however, SCF_ALL has been implemented*/
1086 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1087 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1089 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1090 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1092 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1094 /*Select the string "wine"*/
1095 cr.cpMin = 0;
1096 cr.cpMax = 4;
1097 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1099 /*Copy the italicized "wine" to the clipboard*/
1100 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1102 /*Reset the formatting to default*/
1103 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1104 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1105 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1107 /*Clear the text in the control*/
1108 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1110 /*Switch to Plain Text Mode*/
1111 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1112 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1114 /*Input "wine" again in normal format*/
1115 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1117 /*Paste the italicized "wine" into the control*/
1118 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1120 /*Select a character from the first "wine" string*/
1121 cr.cpMin = 2;
1122 cr.cpMax = 3;
1123 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1125 /*Retrieve its formatting*/
1126 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1127 (LPARAM) &cf2);
1129 /*Select a character from the second "wine" string*/
1130 cr.cpMin = 5;
1131 cr.cpMax = 6;
1132 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1134 /*Retrieve its formatting*/
1135 cf2test.cbSize = sizeof(CHARFORMAT2);
1136 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1137 (LPARAM) &cf2test);
1139 /*Compare the two formattings*/
1140 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1141 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1142 cf2.dwEffects, cf2test.dwEffects);
1143 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1144 printing "wine" in the current format(normal)
1145 pasting "wine" from the clipboard(italicized)
1146 comparing the two formats(should differ)*/
1148 /*Attempt to switch with text in control*/
1149 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1150 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1152 /*Clear control*/
1153 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1155 /*Switch into Rich Text mode*/
1156 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1157 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1159 /*Print "wine" in normal formatting into the control*/
1160 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1162 /*Paste italicized "wine" into the control*/
1163 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1165 /*Select text from the first "wine" string*/
1166 cr.cpMin = 1;
1167 cr.cpMax = 3;
1168 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1170 /*Retrieve its formatting*/
1171 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1172 (LPARAM) &cf2);
1174 /*Select text from the second "wine" string*/
1175 cr.cpMin = 6;
1176 cr.cpMax = 7;
1177 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1179 /*Retrieve its formatting*/
1180 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1181 (LPARAM) &cf2test);
1183 /*Test that the two formattings are not the same*/
1184 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1185 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1186 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1188 DestroyWindow(hwndRichEdit);
1191 static void test_SETPARAFORMAT(void)
1193 HWND hwndRichEdit = new_richedit(NULL);
1194 PARAFORMAT2 fmt;
1195 HRESULT ret;
1196 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1197 fmt.cbSize = sizeof(PARAFORMAT2);
1198 fmt.dwMask = PFM_ALIGNMENT;
1199 fmt.wAlignment = PFA_LEFT;
1201 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1202 ok(ret != 0, "expected non-zero got %d\n", ret);
1204 fmt.cbSize = sizeof(PARAFORMAT2);
1205 fmt.dwMask = -1;
1206 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1207 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1208 * between richedit different native builds of riched20.dll
1209 * used on different Windows versions. */
1210 ret &= ~PFM_TABLEROWDELIMITER;
1211 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1213 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1214 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1216 DestroyWindow(hwndRichEdit);
1219 static void test_TM_PLAINTEXT(void)
1221 /*Tests plain text properties*/
1223 HWND hwndRichEdit = new_richedit(NULL);
1224 CHARFORMAT2 cf2, cf2test;
1225 CHARRANGE cr;
1226 int rc = 0;
1228 /*Switch to plain text mode*/
1230 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1231 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1233 /*Fill control with text*/
1235 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1237 /*Select some text and bold it*/
1239 cr.cpMin = 10;
1240 cr.cpMax = 20;
1241 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1242 cf2.cbSize = sizeof(CHARFORMAT2);
1243 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1244 (LPARAM) &cf2);
1246 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1247 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1249 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1250 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1252 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1253 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1255 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1256 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1258 /*Get the formatting of those characters*/
1260 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1262 /*Get the formatting of some other characters*/
1263 cf2test.cbSize = sizeof(CHARFORMAT2);
1264 cr.cpMin = 21;
1265 cr.cpMax = 30;
1266 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1267 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1269 /*Test that they are the same as plain text allows only one formatting*/
1271 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1272 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1273 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1275 /*Fill the control with a "wine" string, which when inserted will be bold*/
1277 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1279 /*Copy the bolded "wine" string*/
1281 cr.cpMin = 0;
1282 cr.cpMax = 4;
1283 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1284 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1286 /*Swap back to rich text*/
1288 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1289 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1291 /*Set the default formatting to bold italics*/
1293 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1294 cf2.dwMask |= CFM_ITALIC;
1295 cf2.dwEffects ^= CFE_ITALIC;
1296 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1297 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1299 /*Set the text in the control to "wine", which will be bold and italicized*/
1301 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1303 /*Paste the plain text "wine" string, which should take the insert
1304 formatting, which at the moment is bold italics*/
1306 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1308 /*Select the first "wine" string and retrieve its formatting*/
1310 cr.cpMin = 1;
1311 cr.cpMax = 3;
1312 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1313 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1315 /*Select the second "wine" string and retrieve its formatting*/
1317 cr.cpMin = 5;
1318 cr.cpMax = 7;
1319 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1320 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1322 /*Compare the two formattings. They should be the same.*/
1324 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1325 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1326 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1327 DestroyWindow(hwndRichEdit);
1330 static void test_WM_GETTEXT(void)
1332 HWND hwndRichEdit = new_richedit(NULL);
1333 static const char text[] = "Hello. My name is RichEdit!";
1334 static const char text2[] = "Hello. My name is RichEdit!\r";
1335 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1336 char buffer[1024] = {0};
1337 int result;
1339 /* Baseline test with normal-sized buffer */
1340 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1341 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1342 ok(result == lstrlen(buffer),
1343 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1344 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1345 result = strcmp(buffer,text);
1346 ok(result == 0,
1347 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1349 /* Test for returned value of WM_GETTEXTLENGTH */
1350 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1351 ok(result == lstrlen(text),
1352 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1353 result, lstrlen(text));
1355 /* Test for behavior in overflow case */
1356 memset(buffer, 0, 1024);
1357 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1358 ok(result == 0 ||
1359 result == lstrlenA(text) - 1, /* XP, win2k3 */
1360 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1361 result = strcmp(buffer,text);
1362 if (result)
1363 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1364 ok(result == 0,
1365 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1367 /* Baseline test with normal-sized buffer and carriage return */
1368 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1369 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1370 ok(result == lstrlen(buffer),
1371 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1372 result = strcmp(buffer,text2_after);
1373 ok(result == 0,
1374 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1376 /* Test for returned value of WM_GETTEXTLENGTH */
1377 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1378 ok(result == lstrlen(text2_after),
1379 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1380 result, lstrlen(text2_after));
1382 /* Test for behavior of CRLF conversion in case of overflow */
1383 memset(buffer, 0, 1024);
1384 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1385 ok(result == 0 ||
1386 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1387 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1388 result = strcmp(buffer,text2);
1389 if (result)
1390 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1391 ok(result == 0,
1392 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1394 DestroyWindow(hwndRichEdit);
1397 static void test_EM_GETTEXTRANGE(void)
1399 HWND hwndRichEdit = new_richedit(NULL);
1400 const char * text1 = "foo bar\r\nfoo bar";
1401 const char * text2 = "foo bar\rfoo bar";
1402 const char * expect = "bar\rfoo";
1403 char buffer[1024] = {0};
1404 LRESULT result;
1405 TEXTRANGEA textRange;
1407 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1409 textRange.lpstrText = buffer;
1410 textRange.chrg.cpMin = 4;
1411 textRange.chrg.cpMax = 11;
1412 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1413 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1414 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1416 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1418 textRange.lpstrText = buffer;
1419 textRange.chrg.cpMin = 4;
1420 textRange.chrg.cpMax = 11;
1421 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1422 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1423 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1425 /* cpMax of text length is used instead of -1 in this case */
1426 textRange.lpstrText = buffer;
1427 textRange.chrg.cpMin = 0;
1428 textRange.chrg.cpMax = -1;
1429 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1430 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1431 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1433 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1434 textRange.lpstrText = buffer;
1435 textRange.chrg.cpMin = -1;
1436 textRange.chrg.cpMax = 1;
1437 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1438 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1439 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1441 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1442 textRange.lpstrText = buffer;
1443 textRange.chrg.cpMin = 1;
1444 textRange.chrg.cpMax = -1;
1445 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1446 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1447 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1449 /* no end character is copied if cpMax - cpMin < 0 */
1450 textRange.lpstrText = buffer;
1451 textRange.chrg.cpMin = 5;
1452 textRange.chrg.cpMax = 5;
1453 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1454 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1455 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1457 /* cpMax of text length is used if cpMax > text length*/
1458 textRange.lpstrText = buffer;
1459 textRange.chrg.cpMin = 0;
1460 textRange.chrg.cpMax = 1000;
1461 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1462 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1463 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1465 DestroyWindow(hwndRichEdit);
1468 static void test_EM_GETSELTEXT(void)
1470 HWND hwndRichEdit = new_richedit(NULL);
1471 const char * text1 = "foo bar\r\nfoo bar";
1472 const char * text2 = "foo bar\rfoo bar";
1473 const char * expect = "bar\rfoo";
1474 char buffer[1024] = {0};
1475 LRESULT result;
1477 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1479 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1480 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1481 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1482 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1484 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1486 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1487 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1488 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1489 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1491 DestroyWindow(hwndRichEdit);
1494 /* FIXME: need to test unimplemented options and robustly test wparam */
1495 static void test_EM_SETOPTIONS(void)
1497 HWND hwndRichEdit;
1498 static const char text[] = "Hello. My name is RichEdit!";
1499 char buffer[1024] = {0};
1500 DWORD dwStyle, options, oldOptions;
1501 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1502 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1503 ES_SELECTIONBAR|ES_VERTICAL;
1505 /* Test initial options. */
1506 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1507 0, 0, 200, 60, NULL, NULL,
1508 hmoduleRichEdit, NULL);
1509 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1510 RICHEDIT_CLASS, (int) GetLastError());
1511 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1512 ok(options == 0, "Incorrect initial options %x\n", options);
1513 DestroyWindow(hwndRichEdit);
1515 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1516 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1517 0, 0, 200, 60, NULL, NULL,
1518 hmoduleRichEdit, NULL);
1519 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1520 RICHEDIT_CLASS, (int) GetLastError());
1521 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1522 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1523 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1524 "Incorrect initial options %x\n", options);
1526 /* NEGATIVE TESTING - NO OPTIONS SET */
1527 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1528 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1530 /* testing no readonly by sending 'a' to the control*/
1531 SetFocus(hwndRichEdit);
1532 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1533 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1534 ok(buffer[0]=='a',
1535 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1536 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1538 /* READONLY - sending 'a' to the control */
1539 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1540 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1541 SetFocus(hwndRichEdit);
1542 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1543 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1544 ok(buffer[0]==text[0],
1545 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1547 /* EM_SETOPTIONS changes the window style, but changing the
1548 * window style does not change the options. */
1549 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1550 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1551 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1552 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1553 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1554 /* Confirm that the text is still read only. */
1555 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1556 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1557 ok(buffer[0]==text[0],
1558 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1560 oldOptions = options;
1561 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1562 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1563 ok(options == oldOptions,
1564 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1566 DestroyWindow(hwndRichEdit);
1569 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1571 CHARFORMAT2W text_format;
1572 text_format.cbSize = sizeof(text_format);
1573 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1574 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1575 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1578 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1580 int link_present = 0;
1582 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1583 if (is_url)
1584 { /* control text is url; should get CFE_LINK */
1585 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1587 else
1589 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1593 static HWND new_static_wnd(HWND parent) {
1594 return new_window("Static", 0, parent);
1597 static void test_EM_AUTOURLDETECT(void)
1599 /* DO NOT change the properties of the first two elements. To shorten the
1600 tests, all tests after WM_SETTEXT test just the first two elements -
1601 one non-URL and one URL */
1602 struct urls_s {
1603 const char *text;
1604 int is_url;
1605 } urls[12] = {
1606 {"winehq.org", 0},
1607 {"http://www.winehq.org", 1},
1608 {"http//winehq.org", 0},
1609 {"ww.winehq.org", 0},
1610 {"www.winehq.org", 1},
1611 {"ftp://192.168.1.1", 1},
1612 {"ftp//192.168.1.1", 0},
1613 {"mailto:your@email.com", 1},
1614 {"prospero:prosperoserver", 1},
1615 {"telnet:test", 1},
1616 {"news:newserver", 1},
1617 {"wais:waisserver", 1}
1620 int i, j;
1621 int urlRet=-1;
1622 HWND hwndRichEdit, parent;
1624 /* All of the following should cause the URL to be detected */
1625 const char * templates_delim[] = {
1626 "This is some text with X on it",
1627 "This is some text with (X) on it",
1628 "This is some text with X\r on it",
1629 "This is some text with ---X--- on it",
1630 "This is some text with \"X\" on it",
1631 "This is some text with 'X' on it",
1632 "This is some text with 'X' on it",
1633 "This is some text with :X: on it",
1635 "This text ends with X",
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\" on it",
1640 "This is some text with X' on it",
1641 "This is some text with X: on it",
1643 "This is some text with (X on it",
1644 "This is some text with \rX on it",
1645 "This is some text with ---X on it",
1646 "This is some text with \"X on it",
1647 "This is some text with 'X on it",
1648 "This is some text with :X on it",
1650 /* None of these should cause the URL to be detected */
1651 const char * templates_non_delim[] = {
1652 "This is some text with |X| on it",
1653 "This is some text with *X* on it",
1654 "This is some text with /X/ on it",
1655 "This is some text with +X+ on it",
1656 "This is some text with %X% on it",
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",
1660 "This is some text with |X on it",
1661 "This is some text with *X on it",
1662 "This is some text with /X on it",
1663 "This is some text with +X on it",
1664 "This is some text with %X on it",
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",
1669 /* All of these cause the URL detection to be extended by one more byte,
1670 thus demonstrating that the tested character is considered as part
1671 of the URL. */
1672 const char * templates_xten_delim[] = {
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",
1679 "This is some text with X@ on it",
1680 "This is some text with X\\ on it",
1682 char buffer[1024];
1684 parent = new_static_wnd(NULL);
1685 hwndRichEdit = new_richedit(parent);
1686 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1687 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1688 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1689 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1690 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1691 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1692 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1693 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1694 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1695 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1696 /* for each url, check the text to see if CFE_LINK effect is present */
1697 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1699 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1700 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1701 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1703 /* Link detection should happen immediately upon WM_SETTEXT */
1704 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1705 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1706 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1708 DestroyWindow(hwndRichEdit);
1710 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1711 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1712 hwndRichEdit = new_richedit(parent);
1714 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1715 char * at_pos;
1716 int at_offset;
1717 int end_offset;
1719 at_pos = strchr(templates_delim[j], 'X');
1720 at_offset = at_pos - templates_delim[j];
1721 strncpy(buffer, templates_delim[j], at_offset);
1722 buffer[at_offset] = '\0';
1723 strcat(buffer, urls[i].text);
1724 strcat(buffer, templates_delim[j] + at_offset + 1);
1725 end_offset = at_offset + strlen(urls[i].text);
1727 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1728 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1730 /* This assumes no templates start with the URL itself, and that they
1731 have at least two characters before the URL text */
1732 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1733 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1734 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1735 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1736 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1737 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1739 if (urls[i].is_url)
1741 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1742 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1743 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1744 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1746 else
1748 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1749 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1750 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1751 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1753 if (buffer[end_offset] != '\0')
1755 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1756 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1757 if (buffer[end_offset +1] != '\0')
1759 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1760 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1765 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1766 char * at_pos;
1767 int at_offset;
1768 int end_offset;
1770 at_pos = strchr(templates_non_delim[j], 'X');
1771 at_offset = at_pos - templates_non_delim[j];
1772 strncpy(buffer, templates_non_delim[j], at_offset);
1773 buffer[at_offset] = '\0';
1774 strcat(buffer, urls[i].text);
1775 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1776 end_offset = at_offset + strlen(urls[i].text);
1778 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1779 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1781 /* This assumes no templates start with the URL itself, and that they
1782 have at least two characters before the URL text */
1783 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1784 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1785 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1786 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1787 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1788 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1790 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1791 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1792 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1793 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1794 if (buffer[end_offset] != '\0')
1796 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1797 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1798 if (buffer[end_offset +1] != '\0')
1800 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1801 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1806 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1807 char * at_pos;
1808 int at_offset;
1809 int end_offset;
1811 at_pos = strchr(templates_xten_delim[j], 'X');
1812 at_offset = at_pos - templates_xten_delim[j];
1813 strncpy(buffer, templates_xten_delim[j], at_offset);
1814 buffer[at_offset] = '\0';
1815 strcat(buffer, urls[i].text);
1816 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1817 end_offset = at_offset + strlen(urls[i].text);
1819 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1820 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1822 /* This assumes no templates start with the URL itself, and that they
1823 have at least two characters before the URL text */
1824 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1825 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1826 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1827 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1828 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1829 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1831 if (urls[i].is_url)
1833 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1834 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1835 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1836 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1837 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1838 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1840 else
1842 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1843 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1844 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1845 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1846 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1847 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1849 if (buffer[end_offset +1] != '\0')
1851 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1852 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1853 if (buffer[end_offset +2] != '\0')
1855 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1856 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1861 DestroyWindow(hwndRichEdit);
1862 hwndRichEdit = NULL;
1865 /* Test detection of URLs within normal text - WM_CHAR case. */
1866 /* Test only the first two URL examples for brevity */
1867 for (i = 0; i < 2; i++) {
1868 hwndRichEdit = new_richedit(parent);
1870 /* Also for brevity, test only the first three delimiters */
1871 for (j = 0; j < 3; j++) {
1872 char * at_pos;
1873 int at_offset;
1874 int end_offset;
1875 int u, v;
1877 at_pos = strchr(templates_delim[j], 'X');
1878 at_offset = at_pos - templates_delim[j];
1879 end_offset = at_offset + strlen(urls[i].text);
1881 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1882 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1883 for (u = 0; templates_delim[j][u]; u++) {
1884 if (templates_delim[j][u] == '\r') {
1885 simulate_typing_characters(hwndRichEdit, "\r");
1886 } else if (templates_delim[j][u] != 'X') {
1887 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1888 } else {
1889 for (v = 0; urls[i].text[v]; v++) {
1890 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1894 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1896 /* This assumes no templates start with the URL itself, and that they
1897 have at least two characters before the URL text */
1898 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1899 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1900 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1901 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1902 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1903 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1905 if (urls[i].is_url)
1907 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1908 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1909 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1910 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1912 else
1914 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1915 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1916 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1917 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1919 if (buffer[end_offset] != '\0')
1921 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1922 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1923 if (buffer[end_offset +1] != '\0')
1925 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1926 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1930 /* The following will insert a paragraph break after the first character
1931 of the URL candidate, thus breaking the URL. It is expected that the
1932 CFE_LINK attribute should break across both pieces of the URL */
1933 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1934 simulate_typing_characters(hwndRichEdit, "\r");
1935 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1937 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1938 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1939 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1940 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1941 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1942 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1944 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1945 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1946 /* end_offset moved because of paragraph break */
1947 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1948 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1949 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1950 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1952 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1953 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1954 if (buffer[end_offset +2] != '\0')
1956 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1957 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1961 /* The following will remove the just-inserted paragraph break, thus
1962 restoring the URL */
1963 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1964 simulate_typing_characters(hwndRichEdit, "\b");
1965 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1967 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1968 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1969 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1970 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1971 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1972 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1974 if (urls[i].is_url)
1976 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1977 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1978 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1979 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1981 else
1983 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1984 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1985 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1986 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1988 if (buffer[end_offset] != '\0')
1990 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1991 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1992 if (buffer[end_offset +1] != '\0')
1994 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1995 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1999 DestroyWindow(hwndRichEdit);
2000 hwndRichEdit = NULL;
2003 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2004 /* Test just the first two URL examples for brevity */
2005 for (i = 0; i < 2; i++) {
2006 SETTEXTEX st;
2008 hwndRichEdit = new_richedit(parent);
2010 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2011 be detected:
2012 1) Set entire text, a la WM_SETTEXT
2013 2) Set a selection of the text to the URL
2014 3) Set a portion of the text at a time, which eventually results in
2015 an URL
2016 All of them should give equivalent results
2019 /* Set entire text in one go, like WM_SETTEXT */
2020 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2021 char * at_pos;
2022 int at_offset;
2023 int end_offset;
2025 st.codepage = CP_ACP;
2026 st.flags = ST_DEFAULT;
2028 at_pos = strchr(templates_delim[j], 'X');
2029 at_offset = at_pos - templates_delim[j];
2030 strncpy(buffer, templates_delim[j], at_offset);
2031 buffer[at_offset] = '\0';
2032 strcat(buffer, urls[i].text);
2033 strcat(buffer, templates_delim[j] + at_offset + 1);
2034 end_offset = at_offset + strlen(urls[i].text);
2036 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2037 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2039 /* This assumes no templates start with the URL itself, and that they
2040 have at least two characters before the URL text */
2041 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2042 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2043 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2044 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2045 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2046 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2048 if (urls[i].is_url)
2050 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2051 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2052 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2053 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2055 else
2057 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2058 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2059 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2060 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2062 if (buffer[end_offset] != '\0')
2064 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2065 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2066 if (buffer[end_offset +1] != '\0')
2068 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2069 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2074 /* Set selection with X to the URL */
2075 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2076 char * at_pos;
2077 int at_offset;
2078 int end_offset;
2080 at_pos = strchr(templates_delim[j], 'X');
2081 at_offset = at_pos - templates_delim[j];
2082 end_offset = at_offset + strlen(urls[i].text);
2084 st.codepage = CP_ACP;
2085 st.flags = ST_DEFAULT;
2086 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2087 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2088 st.flags = ST_SELECTION;
2089 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2090 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2091 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2093 /* This assumes no templates start with the URL itself, and that they
2094 have at least two characters before the URL text */
2095 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2096 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2097 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2098 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2099 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2100 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2102 if (urls[i].is_url)
2104 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2105 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2106 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2107 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2109 else
2111 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2112 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2113 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2114 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2116 if (buffer[end_offset] != '\0')
2118 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2119 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2120 if (buffer[end_offset +1] != '\0')
2122 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2123 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2128 /* Set selection with X to the first character of the URL, then the rest */
2129 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2130 char * at_pos;
2131 int at_offset;
2132 int end_offset;
2134 at_pos = strchr(templates_delim[j], 'X');
2135 at_offset = at_pos - templates_delim[j];
2136 end_offset = at_offset + strlen(urls[i].text);
2138 strcpy(buffer, "YY");
2139 buffer[0] = urls[i].text[0];
2141 st.codepage = CP_ACP;
2142 st.flags = ST_DEFAULT;
2143 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2144 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2145 st.flags = ST_SELECTION;
2146 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2147 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2148 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2149 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2150 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2152 /* This assumes no templates start with the URL itself, and that they
2153 have at least two characters before the URL text */
2154 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2155 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2156 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2157 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2158 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2159 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2161 if (urls[i].is_url)
2163 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2164 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2165 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2166 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2168 else
2170 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2171 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2172 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2173 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2175 if (buffer[end_offset] != '\0')
2177 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2178 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2179 if (buffer[end_offset +1] != '\0')
2181 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2182 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2187 DestroyWindow(hwndRichEdit);
2188 hwndRichEdit = NULL;
2191 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2192 /* Test just the first two URL examples for brevity */
2193 for (i = 0; i < 2; i++) {
2194 hwndRichEdit = new_richedit(parent);
2196 /* Set selection with X to the URL */
2197 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2198 char * at_pos;
2199 int at_offset;
2200 int end_offset;
2202 at_pos = strchr(templates_delim[j], 'X');
2203 at_offset = at_pos - templates_delim[j];
2204 end_offset = at_offset + strlen(urls[i].text);
2206 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2207 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2208 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2209 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2210 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2212 /* This assumes no templates start with the URL itself, and that they
2213 have at least two characters before the URL text */
2214 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2215 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2216 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2217 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2218 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2219 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2221 if (urls[i].is_url)
2223 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2224 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2225 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2226 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2228 else
2230 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2231 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2232 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2233 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2235 if (buffer[end_offset] != '\0')
2237 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2238 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2239 if (buffer[end_offset +1] != '\0')
2241 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2242 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2247 /* Set selection with X to the first character of the URL, then the rest */
2248 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2249 char * at_pos;
2250 int at_offset;
2251 int end_offset;
2253 at_pos = strchr(templates_delim[j], 'X');
2254 at_offset = at_pos - templates_delim[j];
2255 end_offset = at_offset + strlen(urls[i].text);
2257 strcpy(buffer, "YY");
2258 buffer[0] = urls[i].text[0];
2260 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2261 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2262 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2263 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2264 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2265 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2266 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2268 /* This assumes no templates start with the URL itself, and that they
2269 have at least two characters before the URL text */
2270 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2271 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2272 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2273 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2274 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2275 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2277 if (urls[i].is_url)
2279 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2280 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2281 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2282 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2284 else
2286 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2287 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2288 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2289 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2291 if (buffer[end_offset] != '\0')
2293 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2294 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2295 if (buffer[end_offset +1] != '\0')
2297 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2298 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2303 DestroyWindow(hwndRichEdit);
2304 hwndRichEdit = NULL;
2307 DestroyWindow(parent);
2310 static void test_EM_SCROLL(void)
2312 int i, j;
2313 int r; /* return value */
2314 int expr; /* expected return value */
2315 HWND hwndRichEdit = new_richedit(NULL);
2316 int y_before, y_after; /* units of lines of text */
2318 /* test a richedit box containing a single line of text */
2319 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2320 expr = 0x00010000;
2321 for (i = 0; i < 4; i++) {
2322 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2324 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2325 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2326 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2327 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2328 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2329 "(i == %d)\n", y_after, i);
2333 * test a richedit box that will scroll. There are two general
2334 * cases: the case without any long lines and the case with a long
2335 * line.
2337 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2338 if (i == 0)
2339 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2340 else
2341 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2342 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2343 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2344 "LONG LINE \nb\nc\nd\ne");
2345 for (j = 0; j < 12; j++) /* reset scroll position to top */
2346 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2348 /* get first visible line */
2349 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2350 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2352 /* get new current first visible line */
2353 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2355 ok(((r & 0xffffff00) == 0x00010000) &&
2356 ((r & 0x000000ff) != 0x00000000),
2357 "EM_SCROLL page down didn't scroll by a small positive number of "
2358 "lines (r == 0x%08x)\n", r);
2359 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2360 "(line %d scrolled to line %d\n", y_before, y_after);
2362 y_before = y_after;
2364 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2365 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2366 ok(((r & 0xffffff00) == 0x0001ff00),
2367 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2368 "(r == 0x%08x)\n", r);
2369 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2370 "%d scrolled to line %d\n", y_before, y_after);
2372 y_before = y_after;
2374 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2376 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2378 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2379 "(r == 0x%08x)\n", r);
2380 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2381 "1 line (%d scrolled to %d)\n", y_before, y_after);
2383 y_before = y_after;
2385 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2387 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2389 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2390 "(r == 0x%08x)\n", r);
2391 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2392 "line (%d scrolled to %d)\n", y_before, y_after);
2394 y_before = y_after;
2396 r = SendMessage(hwndRichEdit, EM_SCROLL,
2397 SB_LINEUP, 0); /* lineup beyond top */
2399 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2401 ok(r == 0x00010000,
2402 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2403 ok(y_before == y_after,
2404 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2406 y_before = y_after;
2408 r = SendMessage(hwndRichEdit, EM_SCROLL,
2409 SB_PAGEUP, 0);/*page up beyond top */
2411 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2413 ok(r == 0x00010000,
2414 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2415 ok(y_before == y_after,
2416 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2418 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2419 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2420 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2421 r = SendMessage(hwndRichEdit, EM_SCROLL,
2422 SB_PAGEDOWN, 0); /* page down beyond bot */
2423 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2425 ok(r == 0x00010000,
2426 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2427 ok(y_before == y_after,
2428 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2429 y_before, y_after);
2431 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2432 SendMessage(hwndRichEdit, EM_SCROLL,
2433 SB_LINEDOWN, 0); /* line down beyond bot */
2434 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2436 ok(r == 0x00010000,
2437 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2438 ok(y_before == y_after,
2439 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2440 y_before, y_after);
2442 DestroyWindow(hwndRichEdit);
2445 unsigned int recursionLevel = 0;
2446 unsigned int WM_SIZE_recursionLevel = 0;
2447 BOOL bailedOutOfRecursion = FALSE;
2448 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2450 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2452 LRESULT r;
2454 if (bailedOutOfRecursion) return 0;
2455 if (recursionLevel >= 32) {
2456 bailedOutOfRecursion = TRUE;
2457 return 0;
2460 recursionLevel++;
2461 switch (message) {
2462 case WM_SIZE:
2463 WM_SIZE_recursionLevel++;
2464 r = richeditProc(hwnd, message, wParam, lParam);
2465 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2466 ShowScrollBar(hwnd, SB_VERT, TRUE);
2467 WM_SIZE_recursionLevel--;
2468 break;
2469 default:
2470 r = richeditProc(hwnd, message, wParam, lParam);
2471 break;
2473 recursionLevel--;
2474 return r;
2477 static void test_scrollbar_visibility(void)
2479 HWND hwndRichEdit;
2480 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2481 SCROLLINFO si;
2482 WNDCLASSA cls;
2483 BOOL r;
2485 /* These tests show that richedit should temporarily refrain from automatically
2486 hiding or showing its scrollbars (vertical at least) when an explicit request
2487 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2488 Some applications depend on forced showing (when otherwise richedit would
2489 hide the vertical scrollbar) and are thrown on an endless recursive loop
2490 if richedit auto-hides the scrollbar again. Apparently they never heard of
2491 the ES_DISABLENOSCROLL style... */
2493 hwndRichEdit = new_richedit(NULL);
2495 /* Test default scrollbar visibility behavior */
2496 memset(&si, 0, sizeof(si));
2497 si.cbSize = sizeof(si);
2498 si.fMask = SIF_PAGE | SIF_RANGE;
2499 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2500 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2501 "Vertical scrollbar is visible, should be invisible.\n");
2502 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2503 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2504 si.nPage, si.nMin, si.nMax);
2506 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
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, (LPARAM)text);
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 invisible, should be visible.\n");
2524 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2525 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2526 si.nPage, si.nMin, si.nMax);
2528 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2529 even though it hides the scrollbar */
2530 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2531 memset(&si, 0, sizeof(si));
2532 si.cbSize = sizeof(si);
2533 si.fMask = SIF_PAGE | SIF_RANGE;
2534 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2535 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2536 "Vertical scrollbar is visible, should be invisible.\n");
2537 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2538 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2539 si.nPage, si.nMin, si.nMax);
2541 /* Setting non-scrolling text again does *not* reset scrollbar range */
2542 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2543 memset(&si, 0, sizeof(si));
2544 si.cbSize = sizeof(si);
2545 si.fMask = SIF_PAGE | SIF_RANGE;
2546 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2547 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2548 "Vertical scrollbar is visible, should be invisible.\n");
2549 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2550 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2551 si.nPage, si.nMin, si.nMax);
2553 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2554 memset(&si, 0, sizeof(si));
2555 si.cbSize = sizeof(si);
2556 si.fMask = SIF_PAGE | SIF_RANGE;
2557 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2558 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2559 "Vertical scrollbar is visible, should be invisible.\n");
2560 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2561 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2562 si.nPage, si.nMin, si.nMax);
2564 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
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)"");
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 DestroyWindow(hwndRichEdit);
2588 /* Test again, with ES_DISABLENOSCROLL style */
2589 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2591 /* Test default scrollbar visibility behavior */
2592 memset(&si, 0, sizeof(si));
2593 si.cbSize = sizeof(si);
2594 si.fMask = SIF_PAGE | SIF_RANGE;
2595 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2596 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2597 "Vertical scrollbar is invisible, should be visible.\n");
2598 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2599 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2600 si.nPage, si.nMin, si.nMax);
2602 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
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, (LPARAM)text);
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)\n",
2622 si.nPage, si.nMin, si.nMax);
2624 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2625 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2626 memset(&si, 0, sizeof(si));
2627 si.cbSize = sizeof(si);
2628 si.fMask = SIF_PAGE | SIF_RANGE;
2629 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2630 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2631 "Vertical scrollbar is invisible, should be visible.\n");
2632 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2633 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2634 si.nPage, si.nMin, si.nMax);
2636 /* Setting non-scrolling text again does *not* reset scrollbar range */
2637 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2638 memset(&si, 0, sizeof(si));
2639 si.cbSize = sizeof(si);
2640 si.fMask = SIF_PAGE | SIF_RANGE;
2641 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2642 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2643 "Vertical scrollbar is invisible, should be visible.\n");
2644 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2645 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2646 si.nPage, si.nMin, si.nMax);
2648 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2649 memset(&si, 0, sizeof(si));
2650 si.cbSize = sizeof(si);
2651 si.fMask = SIF_PAGE | SIF_RANGE;
2652 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2653 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2654 "Vertical scrollbar is invisible, should be visible.\n");
2655 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2656 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2657 si.nPage, si.nMin, si.nMax);
2659 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
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)"");
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 DestroyWindow(hwndRichEdit);
2683 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2684 hwndRichEdit = new_richedit(NULL);
2686 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2687 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2688 memset(&si, 0, sizeof(si));
2689 si.cbSize = sizeof(si);
2690 si.fMask = SIF_PAGE | SIF_RANGE;
2691 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2692 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2693 "Vertical scrollbar is invisible, should be visible.\n");
2694 todo_wine {
2695 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2696 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2697 si.nPage, si.nMin, si.nMax);
2700 /* Ditto, see above */
2701 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2702 memset(&si, 0, sizeof(si));
2703 si.cbSize = sizeof(si);
2704 si.fMask = SIF_PAGE | SIF_RANGE;
2705 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2706 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2707 "Vertical scrollbar is invisible, should be visible.\n");
2708 todo_wine {
2709 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2710 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2711 si.nPage, si.nMin, si.nMax);
2714 /* Ditto, see above */
2715 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2716 memset(&si, 0, sizeof(si));
2717 si.cbSize = sizeof(si);
2718 si.fMask = SIF_PAGE | SIF_RANGE;
2719 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2720 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2721 "Vertical scrollbar is invisible, should be visible.\n");
2722 todo_wine {
2723 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2724 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2725 si.nPage, si.nMin, si.nMax);
2728 /* Ditto, see above */
2729 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2730 memset(&si, 0, sizeof(si));
2731 si.cbSize = sizeof(si);
2732 si.fMask = SIF_PAGE | SIF_RANGE;
2733 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2734 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2735 "Vertical scrollbar is invisible, should be visible.\n");
2736 todo_wine {
2737 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2738 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2739 si.nPage, si.nMin, si.nMax);
2742 /* Ditto, see above */
2743 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2744 memset(&si, 0, sizeof(si));
2745 si.cbSize = sizeof(si);
2746 si.fMask = SIF_PAGE | SIF_RANGE;
2747 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2748 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2749 "Vertical scrollbar is invisible, should be visible.\n");
2750 todo_wine {
2751 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2752 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2753 si.nPage, si.nMin, si.nMax);
2756 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2757 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2758 memset(&si, 0, sizeof(si));
2759 si.cbSize = sizeof(si);
2760 si.fMask = SIF_PAGE | SIF_RANGE;
2761 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2762 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2763 "Vertical scrollbar is visible, should be invisible.\n");
2764 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2765 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2766 si.nPage, si.nMin, si.nMax);
2768 DestroyWindow(hwndRichEdit);
2770 hwndRichEdit = new_richedit(NULL);
2772 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2773 memset(&si, 0, sizeof(si));
2774 si.cbSize = sizeof(si);
2775 si.fMask = SIF_PAGE | SIF_RANGE;
2776 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2777 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2778 "Vertical scrollbar is visible, should be invisible.\n");
2779 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2780 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2781 si.nPage, si.nMin, si.nMax);
2783 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
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, (LPARAM)"a");
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, 0);
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, (LPARAM)text);
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 invisible, should be visible.\n");
2823 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2824 "reported page/range is %d (%d..%d)\n",
2825 si.nPage, si.nMin, si.nMax);
2827 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2828 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2829 memset(&si, 0, sizeof(si));
2830 si.cbSize = sizeof(si);
2831 si.fMask = SIF_PAGE | SIF_RANGE;
2832 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2833 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2834 "Vertical scrollbar is visible, should be invisible.\n");
2835 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2836 "reported page/range is %d (%d..%d)\n",
2837 si.nPage, si.nMin, si.nMax);
2839 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2840 memset(&si, 0, sizeof(si));
2841 si.cbSize = sizeof(si);
2842 si.fMask = SIF_PAGE | SIF_RANGE;
2843 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2844 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2845 "Vertical scrollbar is 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 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2851 EM_SCROLL will make visible any forcefully invisible scrollbar */
2852 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2853 memset(&si, 0, sizeof(si));
2854 si.cbSize = sizeof(si);
2855 si.fMask = SIF_PAGE | SIF_RANGE;
2856 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2857 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2858 "Vertical scrollbar is invisible, should be visible.\n");
2859 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2860 "reported page/range is %d (%d..%d)\n",
2861 si.nPage, si.nMin, si.nMax);
2863 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
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 visible, should be invisible.\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 /* Again, EM_SCROLL, with SB_LINEUP */
2875 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2876 memset(&si, 0, sizeof(si));
2877 si.cbSize = sizeof(si);
2878 si.fMask = SIF_PAGE | SIF_RANGE;
2879 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2880 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2881 "Vertical scrollbar is invisible, should be visible.\n");
2882 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2883 "reported page/range is %d (%d..%d)\n",
2884 si.nPage, si.nMin, si.nMax);
2886 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 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 visible, should be invisible.\n");
2893 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2894 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2895 si.nPage, si.nMin, si.nMax);
2897 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
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 invisible, should be visible.\n");
2904 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2905 "reported page/range is %d (%d..%d)\n",
2906 si.nPage, si.nMin, si.nMax);
2908 DestroyWindow(hwndRichEdit);
2911 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2912 hwndRichEdit = new_richedit(NULL);
2914 #define ENABLE_WS_VSCROLL(hwnd) \
2915 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2916 #define DISABLE_WS_VSCROLL(hwnd) \
2917 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2919 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2920 ENABLE_WS_VSCROLL(hwndRichEdit);
2921 memset(&si, 0, sizeof(si));
2922 si.cbSize = sizeof(si);
2923 si.fMask = SIF_PAGE | SIF_RANGE;
2924 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2925 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2926 "Vertical scrollbar is invisible, should be visible.\n");
2927 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2928 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2929 si.nPage, si.nMin, si.nMax);
2931 /* Ditto, see above */
2932 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2933 memset(&si, 0, sizeof(si));
2934 si.cbSize = sizeof(si);
2935 si.fMask = SIF_PAGE | SIF_RANGE;
2936 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2937 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2938 "Vertical scrollbar is invisible, should be visible.\n");
2939 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2940 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2941 si.nPage, si.nMin, si.nMax);
2943 /* Ditto, see above */
2944 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2945 memset(&si, 0, sizeof(si));
2946 si.cbSize = sizeof(si);
2947 si.fMask = SIF_PAGE | SIF_RANGE;
2948 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2949 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2950 "Vertical scrollbar is invisible, should be visible.\n");
2951 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2952 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2953 si.nPage, si.nMin, si.nMax);
2955 /* Ditto, see above */
2956 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2957 memset(&si, 0, sizeof(si));
2958 si.cbSize = sizeof(si);
2959 si.fMask = SIF_PAGE | SIF_RANGE;
2960 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2961 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2962 "Vertical scrollbar is invisible, should be visible.\n");
2963 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2964 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2965 si.nPage, si.nMin, si.nMax);
2967 /* Ditto, see above */
2968 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2969 memset(&si, 0, sizeof(si));
2970 si.cbSize = sizeof(si);
2971 si.fMask = SIF_PAGE | SIF_RANGE;
2972 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2973 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2974 "Vertical scrollbar is invisible, should be visible.\n");
2975 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2976 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2977 si.nPage, si.nMin, si.nMax);
2979 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2980 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2981 memset(&si, 0, sizeof(si));
2982 si.cbSize = sizeof(si);
2983 si.fMask = SIF_PAGE | SIF_RANGE;
2984 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2985 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2986 "Vertical scrollbar is visible, should be invisible.\n");
2987 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2988 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2989 si.nPage, si.nMin, si.nMax);
2991 DestroyWindow(hwndRichEdit);
2993 hwndRichEdit = new_richedit(NULL);
2995 DISABLE_WS_VSCROLL(hwndRichEdit);
2996 memset(&si, 0, sizeof(si));
2997 si.cbSize = sizeof(si);
2998 si.fMask = SIF_PAGE | SIF_RANGE;
2999 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3000 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3001 "Vertical scrollbar is visible, should be invisible.\n");
3002 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3003 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3004 si.nPage, si.nMin, si.nMax);
3006 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
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, (LPARAM)"a");
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, 0);
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, (LPARAM)text);
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 invisible, should be visible.\n");
3046 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3047 "reported page/range is %d (%d..%d)\n",
3048 si.nPage, si.nMin, si.nMax);
3050 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3051 DISABLE_WS_VSCROLL(hwndRichEdit);
3052 memset(&si, 0, sizeof(si));
3053 si.cbSize = sizeof(si);
3054 si.fMask = SIF_PAGE | SIF_RANGE;
3055 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3056 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3057 "Vertical scrollbar is visible, should be invisible.\n");
3058 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3059 "reported page/range is %d (%d..%d)\n",
3060 si.nPage, si.nMin, si.nMax);
3062 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
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) expected nMax/nPage nonzero\n",
3071 si.nPage, si.nMin, si.nMax);
3073 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
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 invisible, should be visible.\n");
3080 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3081 "reported page/range is %d (%d..%d)\n",
3082 si.nPage, si.nMin, si.nMax);
3084 DISABLE_WS_VSCROLL(hwndRichEdit);
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 visible, should be invisible.\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 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3096 EM_SCROLL will make visible any forcefully invisible scrollbar */
3097 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3098 memset(&si, 0, sizeof(si));
3099 si.cbSize = sizeof(si);
3100 si.fMask = SIF_PAGE | SIF_RANGE;
3101 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3102 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3103 "Vertical scrollbar is invisible, should be visible.\n");
3104 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3105 "reported page/range is %d (%d..%d)\n",
3106 si.nPage, si.nMin, si.nMax);
3108 DISABLE_WS_VSCROLL(hwndRichEdit);
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 visible, should be invisible.\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 /* Again, EM_SCROLL, with SB_LINEUP */
3120 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3121 memset(&si, 0, sizeof(si));
3122 si.cbSize = sizeof(si);
3123 si.fMask = SIF_PAGE | SIF_RANGE;
3124 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3125 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3126 "Vertical scrollbar is invisible, should be visible.\n");
3127 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3128 "reported page/range is %d (%d..%d)\n",
3129 si.nPage, si.nMin, si.nMax);
3131 DestroyWindow(hwndRichEdit);
3133 /* This window proc models what is going on with Corman Lisp 3.0.
3134 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3135 force the scrollbar into visibility. Recursion should NOT happen
3136 as a result of this action.
3138 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3139 if (r) {
3140 richeditProc = cls.lpfnWndProc;
3141 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3142 cls.lpszClassName = "RicheditStupidOverride";
3143 if(!RegisterClassA(&cls)) assert(0);
3145 recursionLevel = 0;
3146 WM_SIZE_recursionLevel = 0;
3147 bailedOutOfRecursion = FALSE;
3148 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3149 ok(!bailedOutOfRecursion,
3150 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3152 recursionLevel = 0;
3153 WM_SIZE_recursionLevel = 0;
3154 bailedOutOfRecursion = FALSE;
3155 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3156 ok(!bailedOutOfRecursion,
3157 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3159 /* Unblock window in order to process WM_DESTROY */
3160 recursionLevel = 0;
3161 bailedOutOfRecursion = FALSE;
3162 WM_SIZE_recursionLevel = 0;
3163 DestroyWindow(hwndRichEdit);
3167 static void test_EM_SETUNDOLIMIT(void)
3169 /* cases we test for:
3170 * default behaviour - limiting at 100 undo's
3171 * undo disabled - setting a limit of 0
3172 * undo limited - undo limit set to some to some number, like 2
3173 * bad input - sending a negative number should default to 100 undo's */
3175 HWND hwndRichEdit = new_richedit(NULL);
3176 CHARRANGE cr;
3177 int i;
3178 int result;
3180 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3181 cr.cpMin = 0;
3182 cr.cpMax = 1;
3183 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3184 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3185 also, multiple pastes don't combine like WM_CHAR would */
3186 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3188 /* first case - check the default */
3189 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3190 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3191 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3192 for (i=0; i<100; i++) /* Undo 100 of them */
3193 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3194 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3195 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3197 /* second case - cannot undo */
3198 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3199 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3200 SendMessage(hwndRichEdit,
3201 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3202 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3203 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3205 /* third case - set it to an arbitrary number */
3206 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3207 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3208 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3209 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3210 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3211 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3212 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3213 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3214 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3215 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3216 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3217 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3218 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3219 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3221 /* fourth case - setting negative numbers should default to 100 undos */
3222 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3223 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3224 ok (result == 100,
3225 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3227 DestroyWindow(hwndRichEdit);
3230 static void test_ES_PASSWORD(void)
3232 /* This isn't hugely testable, so we're just going to run it through its paces */
3234 HWND hwndRichEdit = new_richedit(NULL);
3235 WCHAR result;
3237 /* First, check the default of a regular control */
3238 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3239 ok (result == 0,
3240 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3242 /* Now, set it to something normal */
3243 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3244 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3245 ok (result == 120,
3246 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3248 /* Now, set it to something odd */
3249 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3250 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3251 ok (result == 1234,
3252 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3253 DestroyWindow(hwndRichEdit);
3256 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3257 LPBYTE pbBuff,
3258 LONG cb,
3259 LONG *pcb)
3261 char** str = (char**)dwCookie;
3262 *pcb = cb;
3263 if (*pcb > 0) {
3264 memcpy(*str, pbBuff, *pcb);
3265 *str += *pcb;
3267 return 0;
3270 static void test_WM_SETTEXT(void)
3272 HWND hwndRichEdit = new_richedit(NULL);
3273 const char * TestItem1 = "TestSomeText";
3274 const char * TestItem2 = "TestSomeText\r";
3275 const char * TestItem2_after = "TestSomeText\r\n";
3276 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3277 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3278 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3279 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3280 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3281 const char * TestItem5_after = "TestSomeText TestSomeText";
3282 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3283 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3284 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3285 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3287 const char rtftextA[] = "{\\rtf sometext}";
3288 const char urtftextA[] = "{\\urtf sometext}";
3289 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3290 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3291 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3293 char buf[1024] = {0};
3294 WCHAR bufW[1024] = {0};
3295 LRESULT result;
3297 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3298 any solitary \r to be converted to \r\n on return. Properly paired
3299 \r\n are not affected. It also shows that the special sequence \r\r\n
3300 gets converted to a single space.
3303 #define TEST_SETTEXT(a, b) \
3304 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3305 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3306 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3307 ok (result == lstrlen(buf), \
3308 "WM_GETTEXT returned %ld instead of expected %u\n", \
3309 result, lstrlen(buf)); \
3310 result = strcmp(b, buf); \
3311 ok(result == 0, \
3312 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3314 TEST_SETTEXT(TestItem1, TestItem1)
3315 TEST_SETTEXT(TestItem2, TestItem2_after)
3316 TEST_SETTEXT(TestItem3, TestItem3_after)
3317 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3318 TEST_SETTEXT(TestItem4, TestItem4_after)
3319 TEST_SETTEXT(TestItem5, TestItem5_after)
3320 TEST_SETTEXT(TestItem6, TestItem6_after)
3321 TEST_SETTEXT(TestItem7, TestItem7_after)
3323 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3324 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3325 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3326 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3327 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3328 DestroyWindow(hwndRichEdit);
3329 #undef TEST_SETTEXT
3331 #define TEST_SETTEXTW(a, b) \
3332 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3333 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3334 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3335 ok (result == lstrlenW(bufW), \
3336 "WM_GETTEXT returned %ld instead of expected %u\n", \
3337 result, lstrlenW(bufW)); \
3338 result = lstrcmpW(b, bufW); \
3339 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3341 if (is_win9x)
3343 skip("Cannot perform unicode tests\n");
3344 return;
3346 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3347 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3348 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3349 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3350 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3351 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3352 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3353 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3354 DestroyWindow(hwndRichEdit);
3355 #undef TEST_SETTEXTW
3358 static void test_EM_STREAMOUT(void)
3360 HWND hwndRichEdit = new_richedit(NULL);
3361 int r;
3362 EDITSTREAM es;
3363 char buf[1024] = {0};
3364 char * p;
3366 const char * TestItem1 = "TestSomeText";
3367 const char * TestItem2 = "TestSomeText\r";
3368 const char * TestItem3 = "TestSomeText\r\n";
3370 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3371 p = buf;
3372 es.dwCookie = (DWORD_PTR)&p;
3373 es.dwError = 0;
3374 es.pfnCallback = test_WM_SETTEXT_esCallback;
3375 memset(buf, 0, sizeof(buf));
3376 SendMessage(hwndRichEdit, EM_STREAMOUT,
3377 (WPARAM)(SF_TEXT), (LPARAM)&es);
3378 r = strlen(buf);
3379 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3380 ok(strcmp(buf, TestItem1) == 0,
3381 "streamed text different, got %s\n", buf);
3383 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3384 p = buf;
3385 es.dwCookie = (DWORD_PTR)&p;
3386 es.dwError = 0;
3387 es.pfnCallback = test_WM_SETTEXT_esCallback;
3388 memset(buf, 0, sizeof(buf));
3389 SendMessage(hwndRichEdit, EM_STREAMOUT,
3390 (WPARAM)(SF_TEXT), (LPARAM)&es);
3391 r = strlen(buf);
3392 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3393 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3394 ok(strcmp(buf, TestItem3) == 0,
3395 "streamed text different from, got %s\n", buf);
3396 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3397 p = buf;
3398 es.dwCookie = (DWORD_PTR)&p;
3399 es.dwError = 0;
3400 es.pfnCallback = test_WM_SETTEXT_esCallback;
3401 memset(buf, 0, sizeof(buf));
3402 SendMessage(hwndRichEdit, EM_STREAMOUT,
3403 (WPARAM)(SF_TEXT), (LPARAM)&es);
3404 r = strlen(buf);
3405 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3406 ok(strcmp(buf, TestItem3) == 0,
3407 "streamed text different, got %s\n", buf);
3409 DestroyWindow(hwndRichEdit);
3412 static void test_EM_STREAMOUT_FONTTBL(void)
3414 HWND hwndRichEdit = new_richedit(NULL);
3415 EDITSTREAM es;
3416 char buf[1024] = {0};
3417 char * p;
3418 char * fontTbl;
3419 int brackCount;
3421 const char * TestItem = "TestSomeText";
3423 /* fills in the richedit control with some text */
3424 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3426 /* streams out the text in rtf format */
3427 p = buf;
3428 es.dwCookie = (DWORD_PTR)&p;
3429 es.dwError = 0;
3430 es.pfnCallback = test_WM_SETTEXT_esCallback;
3431 memset(buf, 0, sizeof(buf));
3432 SendMessage(hwndRichEdit, EM_STREAMOUT,
3433 (WPARAM)(SF_RTF), (LPARAM)&es);
3435 /* scans for \fonttbl, error if not found */
3436 fontTbl = strstr(buf, "\\fonttbl");
3437 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3438 if(fontTbl)
3440 /* scans for terminating closing bracket */
3441 brackCount = 1;
3442 while(*fontTbl && brackCount)
3444 if(*fontTbl == '{')
3445 brackCount++;
3446 else if(*fontTbl == '}')
3447 brackCount--;
3448 fontTbl++;
3450 /* checks whether closing bracket is ok */
3451 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3452 if(!brackCount)
3454 /* char before closing fonttbl block should be a closed bracket */
3455 fontTbl -= 2;
3456 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3458 /* char after fonttbl block should be a crlf */
3459 fontTbl += 2;
3460 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3463 DestroyWindow(hwndRichEdit);
3467 static void test_EM_SETTEXTEX(void)
3469 HWND hwndRichEdit, parent;
3470 SCROLLINFO si;
3471 int sel_start, sel_end;
3472 SETTEXTEX setText;
3473 GETTEXTEX getText;
3474 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3475 'S', 'o', 'm', 'e',
3476 'T', 'e', 'x', 't', 0};
3477 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3478 't', 'S', 'o', 'm',
3479 'e', 'T', 'e', 'x',
3480 't', 't', 'S', 'o',
3481 'm', 'e', 'T', 'e',
3482 'x', 't', 0};
3483 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3484 '\r','t','S','o','m','e','T','e','x','t',0};
3485 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3486 'S', 'o', 'm', 'e',
3487 'T', 'e', 'x', 't',
3488 '\r', 0};
3489 const char * TestItem2_after = "TestSomeText\r\n";
3490 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3491 'S', 'o', 'm', 'e',
3492 'T', 'e', 'x', 't',
3493 '\r','\n','\r','\n', 0};
3494 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3495 'S', 'o', 'm', 'e',
3496 'T', 'e', 'x', 't',
3497 '\n','\n', 0};
3498 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3499 'S', 'o', 'm', 'e',
3500 'T', 'e', 'x', 't',
3501 '\r','\r', 0};
3502 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3503 'S', 'o', 'm', 'e',
3504 'T', 'e', 'x', 't',
3505 '\r','\r','\n','\r',
3506 '\n', 0};
3507 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3508 'S', 'o', 'm', 'e',
3509 'T', 'e', 'x', 't',
3510 ' ','\r', 0};
3511 #define MAX_BUF_LEN 1024
3512 WCHAR buf[MAX_BUF_LEN];
3513 char bufACP[MAX_BUF_LEN];
3514 char * p;
3515 int result;
3516 CHARRANGE cr;
3517 EDITSTREAM es;
3518 WNDCLASSA cls;
3520 /* Test the scroll position with and without a parent window.
3522 * For some reason the scroll position is 0 after EM_SETTEXTEX
3523 * with the ST_SELECTION flag only when the control has a parent
3524 * window, even though the selection is at the end. */
3525 cls.style = 0;
3526 cls.lpfnWndProc = DefWindowProcA;
3527 cls.cbClsExtra = 0;
3528 cls.cbWndExtra = 0;
3529 cls.hInstance = GetModuleHandleA(0);
3530 cls.hIcon = 0;
3531 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3532 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3533 cls.lpszMenuName = NULL;
3534 cls.lpszClassName = "ParentTestClass";
3535 if(!RegisterClassA(&cls)) assert(0);
3537 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3538 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3539 ok (parent != 0, "Failed to create parent window\n");
3541 hwndRichEdit = CreateWindowEx(0,
3542 RICHEDIT_CLASS, NULL,
3543 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3544 0, 0, 200, 60, parent, NULL,
3545 hmoduleRichEdit, NULL);
3547 setText.codepage = CP_ACP;
3548 setText.flags = ST_SELECTION;
3549 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3550 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3551 si.cbSize = sizeof(si);
3552 si.fMask = SIF_ALL;
3553 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3554 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3555 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3556 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3557 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3559 DestroyWindow(parent);
3561 /* Test without a parent window */
3562 hwndRichEdit = new_richedit(NULL);
3563 setText.codepage = CP_ACP;
3564 setText.flags = ST_SELECTION;
3565 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3566 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3567 si.cbSize = sizeof(si);
3568 si.fMask = SIF_ALL;
3569 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3570 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3571 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3572 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3573 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3575 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3576 * but this time it is because the selection is at the beginning. */
3577 setText.codepage = CP_ACP;
3578 setText.flags = ST_DEFAULT;
3579 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3580 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3581 si.cbSize = sizeof(si);
3582 si.fMask = SIF_ALL;
3583 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3584 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3585 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3586 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3587 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3589 setText.codepage = 1200; /* no constant for unicode */
3590 getText.codepage = 1200; /* no constant for unicode */
3591 getText.cb = MAX_BUF_LEN;
3592 getText.flags = GT_DEFAULT;
3593 getText.lpDefaultChar = NULL;
3594 getText.lpUsedDefChar = NULL;
3596 setText.flags = 0;
3597 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3598 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3599 ok(lstrcmpW(buf, TestItem1) == 0,
3600 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3602 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3603 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3605 setText.codepage = 1200; /* no constant for unicode */
3606 getText.codepage = 1200; /* no constant for unicode */
3607 getText.cb = MAX_BUF_LEN;
3608 getText.flags = GT_DEFAULT;
3609 getText.lpDefaultChar = NULL;
3610 getText.lpUsedDefChar = NULL;
3611 setText.flags = 0;
3612 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3613 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3614 ok(lstrcmpW(buf, TestItem2) == 0,
3615 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3617 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3618 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3619 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3620 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3622 /* Baseline test for just-enough buffer space for string */
3623 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3624 getText.codepage = 1200; /* no constant for unicode */
3625 getText.flags = GT_DEFAULT;
3626 getText.lpDefaultChar = NULL;
3627 getText.lpUsedDefChar = NULL;
3628 memset(buf, 0, MAX_BUF_LEN);
3629 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3630 ok(lstrcmpW(buf, TestItem2) == 0,
3631 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3633 /* When there is enough space for one character, but not both, of the CRLF
3634 pair at the end of the string, the CR is not copied at all. That is,
3635 the caller must not see CRLF pairs truncated to CR at the end of the
3636 string.
3638 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3639 getText.codepage = 1200; /* no constant for unicode */
3640 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3641 getText.lpDefaultChar = NULL;
3642 getText.lpUsedDefChar = NULL;
3643 memset(buf, 0, MAX_BUF_LEN);
3644 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3645 ok(lstrcmpW(buf, TestItem1) == 0,
3646 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3649 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3650 setText.codepage = 1200; /* no constant for unicode */
3651 getText.codepage = 1200; /* no constant for unicode */
3652 getText.cb = MAX_BUF_LEN;
3653 getText.flags = GT_DEFAULT;
3654 getText.lpDefaultChar = NULL;
3655 getText.lpUsedDefChar = NULL;
3656 setText.flags = 0;
3657 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3658 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3659 ok(lstrcmpW(buf, TestItem3_after) == 0,
3660 "EM_SETTEXTEX did not convert properly\n");
3662 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3663 setText.codepage = 1200; /* no constant for unicode */
3664 getText.codepage = 1200; /* no constant for unicode */
3665 getText.cb = MAX_BUF_LEN;
3666 getText.flags = GT_DEFAULT;
3667 getText.lpDefaultChar = NULL;
3668 getText.lpUsedDefChar = NULL;
3669 setText.flags = 0;
3670 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3671 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3672 ok(lstrcmpW(buf, TestItem3_after) == 0,
3673 "EM_SETTEXTEX did not convert properly\n");
3675 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3676 setText.codepage = 1200; /* no constant for unicode */
3677 getText.codepage = 1200; /* no constant for unicode */
3678 getText.cb = MAX_BUF_LEN;
3679 getText.flags = GT_DEFAULT;
3680 getText.lpDefaultChar = NULL;
3681 getText.lpUsedDefChar = NULL;
3682 setText.flags = 0;
3683 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3684 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3685 ok(lstrcmpW(buf, TestItem4_after) == 0,
3686 "EM_SETTEXTEX did not convert properly\n");
3688 /* !ST_SELECTION && Unicode && !\rtf */
3689 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3690 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3692 ok (result == 1,
3693 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3694 ok(lstrlenW(buf) == 0,
3695 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3697 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3698 setText.flags = 0;
3699 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3700 /* select some text */
3701 cr.cpMax = 1;
3702 cr.cpMin = 3;
3703 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3704 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3705 setText.flags = ST_SELECTION;
3706 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3707 ok(result == 0,
3708 "EM_SETTEXTEX with NULL lParam to replace selection"
3709 " with no text should return 0. Got %i\n",
3710 result);
3712 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3713 setText.flags = 0;
3714 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3715 /* select some text */
3716 cr.cpMax = 1;
3717 cr.cpMin = 3;
3718 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3719 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3720 setText.flags = ST_SELECTION;
3721 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3722 (WPARAM)&setText, (LPARAM) TestItem1);
3723 /* get text */
3724 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3725 ok(result == lstrlenW(TestItem1),
3726 "EM_SETTEXTEX with NULL lParam to replace selection"
3727 " with no text should return 0. Got %i\n",
3728 result);
3729 ok(lstrlenW(buf) == 22,
3730 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3731 lstrlenW(buf) );
3733 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3734 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3735 p = (char *)buf;
3736 es.dwCookie = (DWORD_PTR)&p;
3737 es.dwError = 0;
3738 es.pfnCallback = test_WM_SETTEXT_esCallback;
3739 memset(buf, 0, sizeof(buf));
3740 SendMessage(hwndRichEdit, EM_STREAMOUT,
3741 (WPARAM)(SF_RTF), (LPARAM)&es);
3742 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3744 /* !ST_SELECTION && !Unicode && \rtf */
3745 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3746 getText.codepage = 1200; /* no constant for unicode */
3747 getText.cb = MAX_BUF_LEN;
3748 getText.flags = GT_DEFAULT;
3749 getText.lpDefaultChar = NULL;
3750 getText.lpUsedDefChar = NULL;
3752 setText.flags = 0;
3753 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3754 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3755 ok(lstrcmpW(buf, TestItem1) == 0,
3756 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3758 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3759 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3760 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3761 getText.codepage = CP_ACP;
3762 getText.cb = MAX_BUF_LEN;
3763 getText.flags = GT_DEFAULT;
3764 getText.lpDefaultChar = NULL;
3765 getText.lpUsedDefChar = NULL;
3767 setText.flags = ST_SELECTION;
3768 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3769 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3770 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3771 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3772 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3774 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3775 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3776 p = (char *)buf;
3777 es.dwCookie = (DWORD_PTR)&p;
3778 es.dwError = 0;
3779 es.pfnCallback = test_WM_SETTEXT_esCallback;
3780 memset(buf, 0, sizeof(buf));
3781 SendMessage(hwndRichEdit, EM_STREAMOUT,
3782 (WPARAM)(SF_RTF), (LPARAM)&es);
3783 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3785 /* select some text */
3786 cr.cpMax = 1;
3787 cr.cpMin = 3;
3788 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3790 /* ST_SELECTION && !Unicode && \rtf */
3791 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3792 getText.codepage = 1200; /* no constant for unicode */
3793 getText.cb = MAX_BUF_LEN;
3794 getText.flags = GT_DEFAULT;
3795 getText.lpDefaultChar = NULL;
3796 getText.lpUsedDefChar = NULL;
3798 setText.flags = ST_SELECTION;
3799 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3800 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3801 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3803 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3804 setText.codepage = 1200; /* no constant for unicode */
3805 getText.codepage = CP_ACP;
3806 getText.cb = MAX_BUF_LEN;
3808 setText.flags = 0;
3809 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3810 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3812 /* select some text */
3813 cr.cpMax = 1;
3814 cr.cpMin = 3;
3815 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3817 /* ST_SELECTION && !Unicode && !\rtf */
3818 setText.codepage = CP_ACP;
3819 getText.codepage = 1200; /* no constant for unicode */
3820 getText.cb = MAX_BUF_LEN;
3821 getText.flags = GT_DEFAULT;
3822 getText.lpDefaultChar = NULL;
3823 getText.lpUsedDefChar = NULL;
3825 setText.flags = ST_SELECTION;
3826 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3827 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3828 ok(lstrcmpW(buf, TestItem1alt) == 0,
3829 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3830 " using ST_SELECTION and non-Unicode\n");
3832 /* Test setting text using rich text format */
3833 setText.flags = 0;
3834 setText.codepage = CP_ACP;
3835 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3836 getText.codepage = CP_ACP;
3837 getText.cb = MAX_BUF_LEN;
3838 getText.flags = GT_DEFAULT;
3839 getText.lpDefaultChar = NULL;
3840 getText.lpUsedDefChar = NULL;
3841 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3842 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3844 setText.flags = 0;
3845 setText.codepage = CP_ACP;
3846 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3847 getText.codepage = CP_ACP;
3848 getText.cb = MAX_BUF_LEN;
3849 getText.flags = GT_DEFAULT;
3850 getText.lpDefaultChar = NULL;
3851 getText.lpUsedDefChar = NULL;
3852 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3853 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3855 DestroyWindow(hwndRichEdit);
3858 static void test_EM_LIMITTEXT(void)
3860 int ret;
3862 HWND hwndRichEdit = new_richedit(NULL);
3864 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3865 * about setting the length to -1 for multiline edit controls doesn't happen.
3868 /* Don't check default gettextlimit case. That's done in other tests */
3870 /* Set textlimit to 100 */
3871 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3872 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3873 ok (ret == 100,
3874 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3876 /* Set textlimit to 0 */
3877 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3878 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3879 ok (ret == 65536,
3880 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3882 /* Set textlimit to -1 */
3883 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3884 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3885 ok (ret == -1,
3886 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3888 /* Set textlimit to -2 */
3889 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3890 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3891 ok (ret == -2,
3892 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3894 DestroyWindow (hwndRichEdit);
3898 static void test_EM_EXLIMITTEXT(void)
3900 int i, selBegin, selEnd, len1, len2;
3901 int result;
3902 char text[1024 + 1];
3903 char buffer[1024 + 1];
3904 int textlimit = 0; /* multiple of 100 */
3905 HWND hwndRichEdit = new_richedit(NULL);
3907 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3908 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3910 textlimit = 256000;
3911 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3912 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3913 /* set higher */
3914 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3916 textlimit = 1000;
3917 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3918 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3919 /* set lower */
3920 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3922 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3923 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3924 /* default for WParam = 0 */
3925 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3927 textlimit = sizeof(text)-1;
3928 memset(text, 'W', textlimit);
3929 text[sizeof(text)-1] = 0;
3930 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3931 /* maxed out text */
3932 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3934 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3935 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3936 len1 = selEnd - selBegin;
3938 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3939 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3940 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3941 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3942 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3943 len2 = selEnd - selBegin;
3945 ok(len1 != len2,
3946 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3947 len1,len2,i);
3949 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3950 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3951 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3952 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3953 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3954 len1 = selEnd - selBegin;
3956 ok(len1 != len2,
3957 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3958 len1,len2,i);
3960 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3961 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3962 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3963 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3964 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3965 len2 = selEnd - selBegin;
3967 ok(len1 == len2,
3968 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3969 len1,len2,i);
3971 /* set text up to the limit, select all the text, then add a char */
3972 textlimit = 5;
3973 memset(text, 'W', textlimit);
3974 text[textlimit] = 0;
3975 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3976 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3977 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3978 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3979 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3980 result = strcmp(buffer, "A");
3981 ok(0 == result, "got string = \"%s\"\n", buffer);
3983 /* WM_SETTEXT not limited */
3984 textlimit = 10;
3985 memset(text, 'W', textlimit);
3986 text[textlimit] = 0;
3987 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3988 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3989 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3990 i = strlen(buffer);
3991 ok(10 == i, "expected 10 chars\n");
3992 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3993 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3995 /* try inserting more text at end */
3996 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3997 ok(0 == i, "WM_CHAR wasn't processed\n");
3998 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3999 i = strlen(buffer);
4000 ok(10 == i, "expected 10 chars, got %i\n", i);
4001 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4002 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4004 /* try inserting text at beginning */
4005 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4006 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4007 ok(0 == i, "WM_CHAR wasn't processed\n");
4008 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4009 i = strlen(buffer);
4010 ok(10 == i, "expected 10 chars, got %i\n", i);
4011 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4012 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4014 /* WM_CHAR is limited */
4015 textlimit = 1;
4016 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4017 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4018 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4019 ok(0 == i, "WM_CHAR wasn't processed\n");
4020 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4021 ok(0 == i, "WM_CHAR wasn't processed\n");
4022 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4023 i = strlen(buffer);
4024 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4026 DestroyWindow(hwndRichEdit);
4029 static void test_EM_GETLIMITTEXT(void)
4031 int i;
4032 HWND hwndRichEdit = new_richedit(NULL);
4034 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4035 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4037 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4038 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4039 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4041 DestroyWindow(hwndRichEdit);
4044 static void test_WM_SETFONT(void)
4046 /* There is no invalid input or error conditions for this function.
4047 * NULL wParam and lParam just fall back to their default values
4048 * It should be noted that even if you use a gibberish name for your fonts
4049 * here, it will still work because the name is stored. They will display as
4050 * System, but will report their name to be whatever they were created as */
4052 HWND hwndRichEdit = new_richedit(NULL);
4053 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4054 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4055 FF_DONTCARE, "Marlett");
4056 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4057 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4058 FF_DONTCARE, "MS Sans Serif");
4059 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4060 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4061 FF_DONTCARE, "Courier");
4062 LOGFONTA sentLogFont;
4063 CHARFORMAT2A returnedCF2A;
4065 returnedCF2A.cbSize = sizeof(returnedCF2A);
4067 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4068 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4069 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4071 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4072 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4073 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4074 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4076 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4077 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4078 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4079 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4080 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4081 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4083 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4084 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4085 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4086 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4087 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4088 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4090 /* This last test is special since we send in NULL. We clear the variables
4091 * and just compare to "System" instead of the sent in font name. */
4092 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4093 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4094 returnedCF2A.cbSize = sizeof(returnedCF2A);
4096 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4097 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4098 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4099 ok (!strcmp("System",returnedCF2A.szFaceName),
4100 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4102 DestroyWindow(hwndRichEdit);
4106 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4107 LPBYTE pbBuff,
4108 LONG cb,
4109 LONG *pcb)
4111 const char** str = (const char**)dwCookie;
4112 int size = strlen(*str);
4113 if(size > 3) /* let's make it piecemeal for fun */
4114 size = 3;
4115 *pcb = cb;
4116 if (*pcb > size) {
4117 *pcb = size;
4119 if (*pcb > 0) {
4120 memcpy(pbBuff, *str, *pcb);
4121 *str += *pcb;
4123 return 0;
4126 static void test_EM_GETMODIFY(void)
4128 HWND hwndRichEdit = new_richedit(NULL);
4129 LRESULT result;
4130 SETTEXTEX setText;
4131 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4132 'S', 'o', 'm', 'e',
4133 'T', 'e', 'x', 't', 0};
4134 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4135 'S', 'o', 'm', 'e',
4136 'O', 't', 'h', 'e', 'r',
4137 'T', 'e', 'x', 't', 0};
4138 const char* streamText = "hello world";
4139 CHARFORMAT2 cf2;
4140 PARAFORMAT2 pf2;
4141 EDITSTREAM es;
4143 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4144 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4145 FF_DONTCARE, "Courier");
4147 setText.codepage = 1200; /* no constant for unicode */
4148 setText.flags = ST_KEEPUNDO;
4151 /* modify flag shouldn't be set when richedit is first created */
4152 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4153 ok (result == 0,
4154 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4156 /* setting modify flag should actually set it */
4157 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4158 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4159 ok (result != 0,
4160 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4162 /* clearing modify flag should actually clear it */
4163 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4164 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4165 ok (result == 0,
4166 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4168 /* setting font doesn't change modify flag */
4169 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4170 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4171 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4172 ok (result == 0,
4173 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4175 /* setting text should set modify flag */
4176 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4177 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4178 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4179 ok (result != 0,
4180 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4182 /* undo previous text doesn't reset modify flag */
4183 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4184 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4185 ok (result != 0,
4186 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4188 /* set text with no flag to keep undo stack should not set modify flag */
4189 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4190 setText.flags = 0;
4191 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4192 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4193 ok (result == 0,
4194 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4196 /* WM_SETTEXT doesn't modify */
4197 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4198 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4199 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4200 ok (result == 0,
4201 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4203 /* clear the text */
4204 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4205 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4206 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4207 ok (result == 0,
4208 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4210 /* replace text */
4211 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4212 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4213 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4214 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4215 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4216 ok (result != 0,
4217 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4219 /* copy/paste text 1 */
4220 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4221 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4222 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4223 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4224 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4225 ok (result != 0,
4226 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4228 /* copy/paste text 2 */
4229 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4230 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4231 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4232 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4233 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4234 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4235 ok (result != 0,
4236 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4238 /* press char */
4239 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4240 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4241 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4242 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4243 ok (result != 0,
4244 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4246 /* press del */
4247 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4248 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4249 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4250 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4251 ok (result != 0,
4252 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4254 /* set char format */
4255 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4256 cf2.cbSize = sizeof(CHARFORMAT2);
4257 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4258 (LPARAM) &cf2);
4259 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4260 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4261 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4262 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4263 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4264 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4265 ok (result != 0,
4266 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4268 /* set para format */
4269 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4270 pf2.cbSize = sizeof(PARAFORMAT2);
4271 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4272 (LPARAM) &pf2);
4273 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4274 pf2.wAlignment = PFA_RIGHT;
4275 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4276 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4277 ok (result == 0,
4278 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4280 /* EM_STREAM */
4281 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4282 es.dwCookie = (DWORD_PTR)&streamText;
4283 es.dwError = 0;
4284 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4285 SendMessage(hwndRichEdit, EM_STREAMIN,
4286 (WPARAM)(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 long 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 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4435 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4436 returns the number of characters *inserted* into the control (after
4437 required conversions), but WinXP's riched20 returns the number of
4438 characters interpreted from the original lParam. Wine's builtin riched20
4439 implements the WinXP behavior.
4441 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4442 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4443 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4444 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4446 /* Test number of lines reported after EM_REPLACESEL */
4447 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4448 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4450 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4451 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4452 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4453 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4455 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4456 r = strcmp(buffer, "RichEdit1\r\n");
4457 ok(0 == r, "expected %d, got %d\n", 0, r);
4458 getText.cb = 1024;
4459 getText.codepage = CP_ACP;
4460 getText.flags = GT_DEFAULT;
4461 getText.lpDefaultChar = NULL;
4462 getText.lpUsedDefChar = NULL;
4463 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4464 ok(strcmp(buffer, "RichEdit1\r") == 0,
4465 "EM_GETTEXTEX returned incorrect string\n");
4467 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4468 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4469 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4470 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4472 /* The following tests show that richedit should handle the special \r\r\n
4473 sequence by turning it into a single space on insertion. However,
4474 EM_REPLACESEL on WinXP returns the number of characters in the original
4475 string.
4478 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4479 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4480 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4481 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4482 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4483 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4484 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4486 /* Test the actual string */
4487 getText.cb = 1024;
4488 getText.codepage = CP_ACP;
4489 getText.flags = GT_DEFAULT;
4490 getText.lpDefaultChar = NULL;
4491 getText.lpUsedDefChar = NULL;
4492 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4493 ok(strcmp(buffer, "\r\r") == 0,
4494 "EM_GETTEXTEX returned incorrect string\n");
4496 /* Test number of lines reported after EM_REPLACESEL */
4497 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4498 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4500 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4501 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4502 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4503 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4504 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4505 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4506 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4507 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4509 /* Test the actual string */
4510 getText.cb = 1024;
4511 getText.codepage = CP_ACP;
4512 getText.flags = GT_DEFAULT;
4513 getText.lpDefaultChar = NULL;
4514 getText.lpUsedDefChar = NULL;
4515 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4516 ok(strcmp(buffer, " ") == 0,
4517 "EM_GETTEXTEX returned incorrect string\n");
4519 /* Test number of lines reported after EM_REPLACESEL */
4520 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4521 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4523 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4524 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4525 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4526 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4527 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4528 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4529 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4530 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4532 /* Test the actual string */
4533 getText.cb = 1024;
4534 getText.codepage = CP_ACP;
4535 getText.flags = GT_DEFAULT;
4536 getText.lpDefaultChar = NULL;
4537 getText.lpUsedDefChar = NULL;
4538 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4539 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4540 "EM_GETTEXTEX returned incorrect string\n");
4542 /* Test number of lines reported after EM_REPLACESEL */
4543 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4544 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4546 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4547 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4548 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4549 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4550 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4551 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4552 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4553 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4555 /* Test the actual string */
4556 getText.cb = 1024;
4557 getText.codepage = CP_ACP;
4558 getText.flags = GT_DEFAULT;
4559 getText.lpDefaultChar = NULL;
4560 getText.lpUsedDefChar = NULL;
4561 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4562 ok(strcmp(buffer, " \r") == 0,
4563 "EM_GETTEXTEX returned incorrect string\n");
4565 /* Test number of lines reported after EM_REPLACESEL */
4566 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4567 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4569 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4570 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4571 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4572 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4573 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4574 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4575 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4576 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4578 /* Test the actual string */
4579 getText.cb = 1024;
4580 getText.codepage = CP_ACP;
4581 getText.flags = GT_DEFAULT;
4582 getText.lpDefaultChar = NULL;
4583 getText.lpUsedDefChar = NULL;
4584 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4585 ok(strcmp(buffer, " \r\r") == 0,
4586 "EM_GETTEXTEX returned incorrect string\n");
4588 /* Test number of lines reported after EM_REPLACESEL */
4589 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4590 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4592 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4593 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4594 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4595 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4596 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4597 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4598 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4599 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4601 /* Test the actual string */
4602 getText.cb = 1024;
4603 getText.codepage = CP_ACP;
4604 getText.flags = GT_DEFAULT;
4605 getText.lpDefaultChar = NULL;
4606 getText.lpUsedDefChar = NULL;
4607 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4608 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4609 "EM_GETTEXTEX returned incorrect string\n");
4611 /* Test number of lines reported after EM_REPLACESEL */
4612 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4613 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4615 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4616 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4617 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4618 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4619 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4620 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4621 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4623 /* Test the actual string */
4624 getText.cb = 1024;
4625 getText.codepage = CP_ACP;
4626 getText.flags = GT_DEFAULT;
4627 getText.lpDefaultChar = NULL;
4628 getText.lpUsedDefChar = NULL;
4629 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4630 ok(strcmp(buffer, "\r\r") == 0,
4631 "EM_GETTEXTEX returned incorrect string\n");
4633 /* Test number of lines reported after EM_REPLACESEL */
4634 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4635 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4637 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4638 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4639 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4640 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4641 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4642 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4643 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4644 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4646 /* Test the actual string */
4647 getText.cb = 1024;
4648 getText.codepage = CP_ACP;
4649 getText.flags = GT_DEFAULT;
4650 getText.lpDefaultChar = NULL;
4651 getText.lpUsedDefChar = NULL;
4652 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4653 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4654 "EM_GETTEXTEX returned incorrect string\n");
4656 /* Test number of lines reported after EM_REPLACESEL */
4657 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4658 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4660 if (!redraw)
4661 /* This is needed to avoid interferring with keybd_event calls
4662 * on other tests that simulate keyboard events. */
4663 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4665 DestroyWindow(hwndRichEdit);
4668 static void test_WM_PASTE(void)
4670 int result;
4671 char buffer[1024] = {0};
4672 const char* text1 = "testing paste\r";
4673 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4674 const char* text1_after = "testing paste\r\n";
4675 const char* text2 = "testing paste\r\rtesting paste";
4676 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4677 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4678 HWND hwndRichEdit = new_richedit(NULL);
4680 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4681 * to test the state of the modifiers (Ctrl/Alt/Shift).
4683 * Therefore Ctrl-<key> keystrokes need to be simulated with
4684 * keybd_event or by using SetKeyboardState to set the modifiers
4685 * and SendMessage to simulate the keystrokes.
4688 /* Sent keystrokes with keybd_event */
4689 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4690 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4691 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4692 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4693 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4695 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4696 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4698 SEND_CTRL_C(hwndRichEdit); /* Copy */
4699 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4700 SEND_CTRL_V(hwndRichEdit); /* Paste */
4701 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4702 /* Pasted text should be visible at this step */
4703 result = strcmp(text1_step1, buffer);
4704 ok(result == 0,
4705 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4707 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4708 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4709 /* Text should be the same as before (except for \r -> \r\n conversion) */
4710 result = strcmp(text1_after, buffer);
4711 ok(result == 0,
4712 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4714 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4715 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4716 SEND_CTRL_C(hwndRichEdit); /* Copy */
4717 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4718 SEND_CTRL_V(hwndRichEdit); /* Paste */
4719 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4720 /* Pasted text should be visible at this step */
4721 result = strcmp(text3, buffer);
4722 ok(result == 0,
4723 "test paste: strcmp = %i\n", result);
4724 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4725 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4726 /* Text should be the same as before (except for \r -> \r\n conversion) */
4727 result = strcmp(text2_after, buffer);
4728 ok(result == 0,
4729 "test paste: strcmp = %i\n", result);
4730 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4731 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4732 /* Text should revert to post-paste state */
4733 result = strcmp(buffer,text3);
4734 ok(result == 0,
4735 "test paste: strcmp = %i\n", result);
4737 #undef SEND_CTRL_C
4738 #undef SEND_CTRL_X
4739 #undef SEND_CTRL_V
4740 #undef SEND_CTRL_Z
4741 #undef SEND_CTRL_Y
4743 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4744 /* Send WM_CHAR to simulates Ctrl-V */
4745 SendMessage(hwndRichEdit, WM_CHAR, 22,
4746 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4747 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4748 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4749 result = strcmp(buffer,"");
4750 ok(result == 0,
4751 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4753 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4754 * with SetKeyboard state. */
4756 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4757 /* Simulates paste (Ctrl-V) */
4758 hold_key(VK_CONTROL);
4759 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4760 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4761 release_key(VK_CONTROL);
4762 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4763 result = strcmp(buffer,"paste");
4764 ok(result == 0,
4765 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4767 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4768 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4769 /* Simulates copy (Ctrl-C) */
4770 hold_key(VK_CONTROL);
4771 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4772 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4773 release_key(VK_CONTROL);
4774 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4775 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4776 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4777 result = strcmp(buffer,"testing");
4778 ok(result == 0,
4779 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4781 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4782 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4783 /* Simulates select all (Ctrl-A) */
4784 hold_key(VK_CONTROL);
4785 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4786 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4787 /* Simulates select cut (Ctrl-X) */
4788 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4789 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4790 release_key(VK_CONTROL);
4791 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4792 result = strcmp(buffer,"");
4793 ok(result == 0,
4794 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4795 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4796 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4797 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4798 result = strcmp(buffer,"cut\r\n");
4799 todo_wine ok(result == 0,
4800 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4801 /* Simulates undo (Ctrl-Z) */
4802 hold_key(VK_CONTROL);
4803 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4804 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4805 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4806 result = strcmp(buffer,"");
4807 ok(result == 0,
4808 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4809 /* Simulates redo (Ctrl-Y) */
4810 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4811 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4812 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4813 result = strcmp(buffer,"cut\r\n");
4814 todo_wine ok(result == 0,
4815 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4816 release_key(VK_CONTROL);
4818 DestroyWindow(hwndRichEdit);
4821 static void test_EM_FORMATRANGE(void)
4823 int i, tpp_x, tpp_y;
4824 HDC hdc;
4825 HWND hwndRichEdit = new_richedit(NULL);
4826 static const struct {
4827 const char *string; /* The string */
4828 int first; /* First 'pagebreak', 0 for don't care */
4829 int second; /* Second 'pagebreak', 0 for don't care */
4830 } fmtstrings[] = {
4831 {"WINE wine", 0, 0},
4832 {"WINE wineWine", 0, 0},
4833 {"WINE\r\nwine\r\nwine", 5, 10},
4834 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4835 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4838 hdc = GetDC(hwndRichEdit);
4839 ok(hdc != NULL, "Could not get HDC\n");
4841 /* Calculate the twips per pixel */
4842 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4843 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4845 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4847 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4849 FORMATRANGE fr;
4850 GETTEXTLENGTHEX gtl;
4851 SIZE stringsize;
4852 int r, len;
4854 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4856 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4857 gtl.codepage = CP_ACP;
4858 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4860 /* Get some size information for the string */
4861 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4863 /* Define the box to be half the width needed and a bit larger than the height.
4864 * Changes to the width means we have at least 2 pages. Changes to the height
4865 * is done so we can check the changing of fr.rc.bottom.
4867 fr.hdc = fr.hdcTarget = hdc;
4868 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4869 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4870 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4872 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4873 todo_wine {
4874 ok(r == len, "Expected %d, got %d\n", len, r);
4877 /* We know that the page can't hold the full string. See how many characters
4878 * are on the first one
4880 fr.chrg.cpMin = 0;
4881 fr.chrg.cpMax = -1;
4882 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4883 todo_wine {
4884 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4886 if (fmtstrings[i].first)
4887 todo_wine {
4888 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4890 else
4891 ok(r < len, "Expected < %d, got %d\n", len, r);
4893 /* Do another page */
4894 fr.chrg.cpMin = r;
4895 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4896 if (fmtstrings[i].second)
4897 todo_wine {
4898 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4900 else
4901 ok (r < len, "Expected < %d, got %d\n", len, r);
4903 /* There is at least on more page, but we don't care */
4905 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4906 todo_wine {
4907 ok(r == len, "Expected %d, got %d\n", len, r);
4911 ReleaseDC(NULL, hdc);
4912 DestroyWindow(hwndRichEdit);
4915 static int nCallbackCount = 0;
4917 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4918 LONG cb, LONG* pcb)
4920 const char text[] = {'t','e','s','t'};
4922 if (sizeof(text) <= cb)
4924 if ((int)dwCookie != nCallbackCount)
4926 *pcb = 0;
4927 return 0;
4930 memcpy (pbBuff, text, sizeof(text));
4931 *pcb = sizeof(text);
4933 nCallbackCount++;
4935 return 0;
4937 else
4938 return 1; /* indicates callback failed */
4941 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4942 LPBYTE pbBuff,
4943 LONG cb,
4944 LONG *pcb)
4946 const char** str = (const char**)dwCookie;
4947 int size = strlen(*str);
4948 *pcb = cb;
4949 if (*pcb > size) {
4950 *pcb = size;
4952 if (*pcb > 0) {
4953 memcpy(pbBuff, *str, *pcb);
4954 *str += *pcb;
4956 return 0;
4959 struct StringWithLength {
4960 int length;
4961 char *buffer;
4964 /* This callback is used to handled the null characters in a string. */
4965 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4966 LPBYTE pbBuff,
4967 LONG cb,
4968 LONG *pcb)
4970 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4971 int size = str->length;
4972 *pcb = cb;
4973 if (*pcb > size) {
4974 *pcb = size;
4976 if (*pcb > 0) {
4977 memcpy(pbBuff, str->buffer, *pcb);
4978 str->buffer += *pcb;
4979 str->length -= *pcb;
4981 return 0;
4984 static void test_EM_STREAMIN(void)
4986 HWND hwndRichEdit = new_richedit(NULL);
4987 LRESULT result;
4988 EDITSTREAM es;
4989 char buffer[1024] = {0};
4991 const char * streamText0 = "{\\rtf1 TestSomeText}";
4992 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4993 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4995 const char * streamText1 =
4996 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4997 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
4998 "}\r\n";
5000 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5001 const char * streamText2 =
5002 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5003 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5004 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5005 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5006 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5007 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5008 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5010 const char * streamText3 = "RichEdit1";
5012 struct StringWithLength cookieForStream4;
5013 const char * streamText4 =
5014 "This text just needs to be long enough to cause run to be split onto "
5015 "two separate lines and make sure the null terminating character is "
5016 "handled properly.\0";
5017 int length4 = strlen(streamText4) + 1;
5018 cookieForStream4.buffer = (char *)streamText4;
5019 cookieForStream4.length = length4;
5021 /* Minimal test without \par at the end */
5022 es.dwCookie = (DWORD_PTR)&streamText0;
5023 es.dwError = 0;
5024 es.pfnCallback = test_EM_STREAMIN_esCallback;
5025 SendMessage(hwndRichEdit, EM_STREAMIN,
5026 (WPARAM)(SF_RTF), (LPARAM)&es);
5028 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5029 ok (result == 12,
5030 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5031 result = strcmp (buffer,"TestSomeText");
5032 ok (result == 0,
5033 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5034 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5036 /* Native richedit 2.0 ignores last \par */
5037 es.dwCookie = (DWORD_PTR)&streamText0a;
5038 es.dwError = 0;
5039 es.pfnCallback = test_EM_STREAMIN_esCallback;
5040 SendMessage(hwndRichEdit, EM_STREAMIN,
5041 (WPARAM)(SF_RTF), (LPARAM)&es);
5043 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5044 ok (result == 12,
5045 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5046 result = strcmp (buffer,"TestSomeText");
5047 ok (result == 0,
5048 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5049 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5051 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5052 es.dwCookie = (DWORD_PTR)&streamText0b;
5053 es.dwError = 0;
5054 es.pfnCallback = test_EM_STREAMIN_esCallback;
5055 SendMessage(hwndRichEdit, EM_STREAMIN,
5056 (WPARAM)(SF_RTF), (LPARAM)&es);
5058 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5059 ok (result == 14,
5060 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5061 result = strcmp (buffer,"TestSomeText\r\n");
5062 ok (result == 0,
5063 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5064 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5066 es.dwCookie = (DWORD_PTR)&streamText1;
5067 es.dwError = 0;
5068 es.pfnCallback = test_EM_STREAMIN_esCallback;
5069 SendMessage(hwndRichEdit, EM_STREAMIN,
5070 (WPARAM)(SF_RTF), (LPARAM)&es);
5072 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5073 ok (result == 12,
5074 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5075 result = strcmp (buffer,"TestSomeText");
5076 ok (result == 0,
5077 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5078 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5080 es.dwCookie = (DWORD_PTR)&streamText2;
5081 es.dwError = 0;
5082 SendMessage(hwndRichEdit, EM_STREAMIN,
5083 (WPARAM)(SF_RTF), (LPARAM)&es);
5085 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5086 ok (result == 0,
5087 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5088 ok (strlen(buffer) == 0,
5089 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5090 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5092 es.dwCookie = (DWORD_PTR)&streamText3;
5093 es.dwError = 0;
5094 SendMessage(hwndRichEdit, EM_STREAMIN,
5095 (WPARAM)(SF_RTF), (LPARAM)&es);
5097 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5098 ok (result == 0,
5099 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5100 ok (strlen(buffer) == 0,
5101 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5102 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5104 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5105 es.dwError = 0;
5106 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5107 SendMessage(hwndRichEdit, EM_STREAMIN,
5108 (WPARAM)(SF_TEXT), (LPARAM)&es);
5110 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5111 ok (result == length4,
5112 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5113 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5115 DestroyWindow(hwndRichEdit);
5118 static void test_EM_StreamIn_Undo(void)
5120 /* The purpose of this test is to determine when a EM_StreamIn should be
5121 * undoable. This is important because WM_PASTE currently uses StreamIn and
5122 * pasting should always be undoable but streaming isn't always.
5124 * cases to test:
5125 * StreamIn plain text without SFF_SELECTION.
5126 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5127 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5128 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5129 * Feel free to add tests for other text modes or StreamIn things.
5133 HWND hwndRichEdit = new_richedit(NULL);
5134 LRESULT result;
5135 EDITSTREAM es;
5136 char buffer[1024] = {0};
5137 const char randomtext[] = "Some text";
5139 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5141 /* StreamIn, no SFF_SELECTION */
5142 es.dwCookie = nCallbackCount;
5143 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5144 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5145 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5146 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
5147 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5148 result = strcmp (buffer,"test");
5149 ok (result == 0,
5150 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5152 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5153 ok (result == FALSE,
5154 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5156 /* StreamIn, SFF_SELECTION, but nothing selected */
5157 es.dwCookie = nCallbackCount;
5158 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5159 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5160 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5161 SendMessage(hwndRichEdit, EM_STREAMIN,
5162 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5163 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5164 result = strcmp (buffer,"testSome text");
5165 ok (result == 0,
5166 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5168 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5169 ok (result == TRUE,
5170 "EM_STREAMIN with SFF_SELECTION but no selection set "
5171 "should create an undo\n");
5173 /* StreamIn, SFF_SELECTION, with a selection */
5174 es.dwCookie = nCallbackCount;
5175 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5176 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5177 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5178 SendMessage(hwndRichEdit, EM_STREAMIN,
5179 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5180 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5181 result = strcmp (buffer,"Sometesttext");
5182 ok (result == 0,
5183 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5185 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5186 ok (result == TRUE,
5187 "EM_STREAMIN with SFF_SELECTION and selection set "
5188 "should create an undo\n");
5190 DestroyWindow(hwndRichEdit);
5193 static BOOL is_em_settextex_supported(HWND hwnd)
5195 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5196 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5199 static void test_unicode_conversions(void)
5201 static const WCHAR tW[] = {'t',0};
5202 static const WCHAR teW[] = {'t','e',0};
5203 static const WCHAR textW[] = {'t','e','s','t',0};
5204 static const char textA[] = "test";
5205 char bufA[64];
5206 WCHAR bufW[64];
5207 HWND hwnd;
5208 int em_settextex_supported, ret;
5210 #define set_textA(hwnd, wm_set_text, txt) \
5211 do { \
5212 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5213 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5214 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5215 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5216 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5217 } while(0)
5218 #define expect_textA(hwnd, wm_get_text, txt) \
5219 do { \
5220 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5221 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5222 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5223 memset(bufA, 0xAA, sizeof(bufA)); \
5224 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5225 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5226 ret = lstrcmpA(bufA, txt); \
5227 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5228 } while(0)
5230 #define set_textW(hwnd, wm_set_text, txt) \
5231 do { \
5232 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
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 = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5236 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5237 } while(0)
5238 #define expect_textW(hwnd, wm_get_text, txt) \
5239 do { \
5240 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, 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(bufW, 0xAA, sizeof(bufW)); \
5244 if (is_win9x) \
5246 assert(wm_get_text == EM_GETTEXTEX); \
5247 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5248 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5250 else \
5252 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5253 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5255 ret = lstrcmpW(bufW, txt); \
5256 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5257 } while(0)
5258 #define expect_empty(hwnd, wm_get_text) \
5259 do { \
5260 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, 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(bufA, 0xAA, sizeof(bufA)); \
5264 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5265 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5266 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5267 } while(0)
5269 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5270 0, 0, 200, 60, 0, 0, 0, 0);
5271 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5273 ret = IsWindowUnicode(hwnd);
5274 if (is_win9x)
5275 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
5276 else
5277 ok(ret, "RichEdit20W should be unicode under NT\n");
5279 /* EM_SETTEXTEX is supported starting from version 3.0 */
5280 em_settextex_supported = is_em_settextex_supported(hwnd);
5281 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5282 em_settextex_supported ? "" : "NOT ");
5284 expect_empty(hwnd, WM_GETTEXT);
5285 expect_empty(hwnd, EM_GETTEXTEX);
5287 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
5288 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5289 expect_textA(hwnd, WM_GETTEXT, "t");
5290 expect_textA(hwnd, EM_GETTEXTEX, "t");
5291 expect_textW(hwnd, EM_GETTEXTEX, tW);
5293 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
5294 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5295 expect_textA(hwnd, WM_GETTEXT, "te");
5296 expect_textA(hwnd, EM_GETTEXTEX, "te");
5297 expect_textW(hwnd, EM_GETTEXTEX, teW);
5299 set_textA(hwnd, WM_SETTEXT, NULL);
5300 expect_empty(hwnd, WM_GETTEXT);
5301 expect_empty(hwnd, EM_GETTEXTEX);
5303 if (is_win9x)
5304 set_textA(hwnd, WM_SETTEXT, textW);
5305 else
5306 set_textA(hwnd, WM_SETTEXT, textA);
5307 expect_textA(hwnd, WM_GETTEXT, textA);
5308 expect_textA(hwnd, EM_GETTEXTEX, textA);
5309 expect_textW(hwnd, EM_GETTEXTEX, textW);
5311 if (em_settextex_supported)
5313 set_textA(hwnd, EM_SETTEXTEX, textA);
5314 expect_textA(hwnd, WM_GETTEXT, textA);
5315 expect_textA(hwnd, EM_GETTEXTEX, textA);
5316 expect_textW(hwnd, EM_GETTEXTEX, textW);
5319 if (!is_win9x)
5321 set_textW(hwnd, WM_SETTEXT, textW);
5322 expect_textW(hwnd, WM_GETTEXT, textW);
5323 expect_textA(hwnd, WM_GETTEXT, textA);
5324 expect_textW(hwnd, EM_GETTEXTEX, textW);
5325 expect_textA(hwnd, EM_GETTEXTEX, textA);
5327 if (em_settextex_supported)
5329 set_textW(hwnd, EM_SETTEXTEX, textW);
5330 expect_textW(hwnd, WM_GETTEXT, textW);
5331 expect_textA(hwnd, WM_GETTEXT, textA);
5332 expect_textW(hwnd, EM_GETTEXTEX, textW);
5333 expect_textA(hwnd, EM_GETTEXTEX, textA);
5336 DestroyWindow(hwnd);
5338 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5339 0, 0, 200, 60, 0, 0, 0, 0);
5340 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5342 ret = IsWindowUnicode(hwnd);
5343 ok(!ret, "RichEdit20A should NOT be unicode\n");
5345 set_textA(hwnd, WM_SETTEXT, textA);
5346 expect_textA(hwnd, WM_GETTEXT, textA);
5347 expect_textA(hwnd, EM_GETTEXTEX, textA);
5348 expect_textW(hwnd, EM_GETTEXTEX, textW);
5350 if (em_settextex_supported)
5352 set_textA(hwnd, EM_SETTEXTEX, textA);
5353 expect_textA(hwnd, WM_GETTEXT, textA);
5354 expect_textA(hwnd, EM_GETTEXTEX, textA);
5355 expect_textW(hwnd, EM_GETTEXTEX, textW);
5358 if (!is_win9x)
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);
5375 DestroyWindow(hwnd);
5378 static void test_WM_CHAR(void)
5380 HWND hwnd;
5381 int ret;
5382 const char * char_list = "abc\rabc\r";
5383 const char * expected_content_single = "abcabc";
5384 const char * expected_content_multi = "abc\r\nabc\r\n";
5385 char buffer[64] = {0};
5386 const char * p;
5388 /* single-line control must IGNORE carriage returns */
5389 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5390 0, 0, 200, 60, 0, 0, 0, 0);
5391 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5393 p = char_list;
5394 while (*p != '\0') {
5395 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5396 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5397 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5398 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5399 p++;
5402 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5403 ret = strcmp(buffer, expected_content_single);
5404 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5406 DestroyWindow(hwnd);
5408 /* multi-line control inserts CR normally */
5409 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5410 0, 0, 200, 60, 0, 0, 0, 0);
5411 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5413 p = char_list;
5414 while (*p != '\0') {
5415 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5416 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5417 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5418 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5419 p++;
5422 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5423 ret = strcmp(buffer, expected_content_multi);
5424 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5426 DestroyWindow(hwnd);
5429 static void test_EM_GETTEXTLENGTHEX(void)
5431 HWND hwnd;
5432 GETTEXTLENGTHEX gtl;
5433 int ret;
5434 const char * base_string = "base string";
5435 const char * test_string = "a\nb\n\n\r\n";
5436 const char * test_string_after = "a";
5437 const char * test_string_2 = "a\rtest\rstring";
5438 char buffer[64] = {0};
5440 /* single line */
5441 if (!is_win9x)
5442 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5443 0, 0, 200, 60, 0, 0, 0, 0);
5444 else
5445 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5446 0, 0, 200, 60, 0, 0, 0, 0);
5447 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5449 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5450 gtl.codepage = CP_ACP;
5451 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5452 ok(ret == 0, "ret %d\n",ret);
5454 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5455 gtl.codepage = CP_ACP;
5456 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5457 ok(ret == 0, "ret %d\n",ret);
5459 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5461 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
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 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5467 gtl.codepage = CP_ACP;
5468 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5469 ok(ret == strlen(base_string), "ret %d\n",ret);
5471 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5473 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5474 gtl.codepage = CP_ACP;
5475 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5476 ok(ret == 1, "ret %d\n",ret);
5478 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5479 gtl.codepage = CP_ACP;
5480 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5481 ok(ret == 1, "ret %d\n",ret);
5483 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5484 ret = strcmp(buffer, test_string_after);
5485 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5487 DestroyWindow(hwnd);
5489 /* multi line */
5490 if (!is_win9x)
5491 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5492 0, 0, 200, 60, 0, 0, 0, 0);
5493 else
5494 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE,
5495 0, 0, 200, 60, 0, 0, 0, 0);
5496 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5498 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5499 gtl.codepage = CP_ACP;
5500 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5501 ok(ret == 0, "ret %d\n",ret);
5503 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5504 gtl.codepage = CP_ACP;
5505 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5506 ok(ret == 0, "ret %d\n",ret);
5508 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5510 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5511 gtl.codepage = CP_ACP;
5512 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5513 ok(ret == strlen(base_string), "ret %d\n",ret);
5515 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5516 gtl.codepage = CP_ACP;
5517 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5518 ok(ret == strlen(base_string), "ret %d\n",ret);
5520 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5522 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5523 gtl.codepage = CP_ACP;
5524 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5525 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5527 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5528 gtl.codepage = CP_ACP;
5529 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5530 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5532 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5534 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5535 gtl.codepage = CP_ACP;
5536 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5537 ok(ret == 10, "ret %d\n",ret);
5539 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5540 gtl.codepage = CP_ACP;
5541 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5542 ok(ret == 6, "ret %d\n",ret);
5544 DestroyWindow(hwnd);
5548 /* globals that parent and child access when checking event masks & notifications */
5549 static HWND eventMaskEditHwnd = 0;
5550 static int queriedEventMask;
5551 static int watchForEventMask = 0;
5553 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5554 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5556 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5558 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5560 return DefWindowProcA(hwnd, message, wParam, lParam);
5563 /* test event masks in combination with WM_COMMAND */
5564 static void test_eventMask(void)
5566 HWND parent;
5567 int ret, style;
5568 WNDCLASSA cls;
5569 const char text[] = "foo bar\n";
5570 int eventMask;
5572 /* register class to capture WM_COMMAND */
5573 cls.style = 0;
5574 cls.lpfnWndProc = ParentMsgCheckProcA;
5575 cls.cbClsExtra = 0;
5576 cls.cbWndExtra = 0;
5577 cls.hInstance = GetModuleHandleA(0);
5578 cls.hIcon = 0;
5579 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5580 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5581 cls.lpszMenuName = NULL;
5582 cls.lpszClassName = "EventMaskParentClass";
5583 if(!RegisterClassA(&cls)) assert(0);
5585 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5586 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5587 ok (parent != 0, "Failed to create parent window\n");
5589 eventMaskEditHwnd = new_richedit(parent);
5590 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5592 eventMask = ENM_CHANGE | ENM_UPDATE;
5593 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5594 ok(ret == ENM_NONE, "wrong event mask\n");
5595 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5596 ok(ret == eventMask, "failed to set event mask\n");
5598 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5599 queriedEventMask = 0; /* initialize to something other than we expect */
5600 watchForEventMask = EN_CHANGE;
5601 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5602 ok(ret == TRUE, "failed to set text\n");
5603 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5604 notification in response to WM_SETTEXT */
5605 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5606 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5608 /* check to see if EN_CHANGE is sent when redraw is turned off */
5609 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5610 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5611 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5612 /* redraw is disabled by making the window invisible. */
5613 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5614 queriedEventMask = 0; /* initialize to something other than we expect */
5615 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5616 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5617 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5618 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5619 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5621 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5622 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5623 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5624 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5625 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5626 watchForEventMask = EN_UPDATE;
5627 queriedEventMask = 0; /* initialize to something other than we expect */
5628 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5629 ok(queriedEventMask == 0,
5630 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5631 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5632 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5633 queriedEventMask = 0; /* initialize to something other than we expect */
5634 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5635 ok(queriedEventMask == eventMask,
5636 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5639 DestroyWindow(parent);
5642 static int received_WM_NOTIFY = 0;
5643 static int modify_at_WM_NOTIFY = 0;
5644 static BOOL filter_on_WM_NOTIFY = FALSE;
5645 static HWND hwndRichedit_WM_NOTIFY;
5647 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5649 if(message == WM_NOTIFY)
5651 received_WM_NOTIFY = 1;
5652 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5653 if (filter_on_WM_NOTIFY) return TRUE;
5655 return DefWindowProcA(hwnd, message, wParam, lParam);
5658 static void test_WM_NOTIFY(void)
5660 HWND parent;
5661 WNDCLASSA cls;
5662 CHARFORMAT2 cf2;
5663 int sel_start, sel_end;
5665 /* register class to capture WM_NOTIFY */
5666 cls.style = 0;
5667 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5668 cls.cbClsExtra = 0;
5669 cls.cbWndExtra = 0;
5670 cls.hInstance = GetModuleHandleA(0);
5671 cls.hIcon = 0;
5672 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5673 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5674 cls.lpszMenuName = NULL;
5675 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5676 if(!RegisterClassA(&cls)) assert(0);
5678 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5679 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5680 ok (parent != 0, "Failed to create parent window\n");
5682 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5683 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5685 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5687 /* Notifications for selection change should only be sent when selection
5688 actually changes. EM_SETCHARFORMAT is one message that calls
5689 ME_CommitUndo, which should check whether message should be sent */
5690 received_WM_NOTIFY = 0;
5691 cf2.cbSize = sizeof(CHARFORMAT2);
5692 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5693 (LPARAM) &cf2);
5694 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5695 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5696 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5697 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5699 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5700 already at 0. */
5701 received_WM_NOTIFY = 0;
5702 modify_at_WM_NOTIFY = 0;
5703 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5704 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5705 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5707 received_WM_NOTIFY = 0;
5708 modify_at_WM_NOTIFY = 0;
5709 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5710 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5712 received_WM_NOTIFY = 0;
5713 modify_at_WM_NOTIFY = 0;
5714 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5715 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5716 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5718 /* Test for WM_NOTIFY messages with redraw disabled. */
5719 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5720 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5721 received_WM_NOTIFY = 0;
5722 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5723 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5724 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5726 /* Test filtering key events. */
5727 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5728 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5729 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5730 received_WM_NOTIFY = 0;
5731 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5732 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5733 ok(sel_start == 1 && sel_end == 1,
5734 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5735 filter_on_WM_NOTIFY = TRUE;
5736 received_WM_NOTIFY = 0;
5737 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5738 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5739 ok(sel_start == 1 && sel_end == 1,
5740 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5742 /* test with owner set to NULL */
5743 SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5744 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5745 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5746 ok(sel_start == 1 && sel_end == 1,
5747 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5749 DestroyWindow(hwndRichedit_WM_NOTIFY);
5750 DestroyWindow(parent);
5753 static void test_undo_coalescing(void)
5755 HWND hwnd;
5756 int result;
5757 char buffer[64] = {0};
5759 /* multi-line control inserts CR normally */
5760 if (!is_win9x)
5761 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5762 0, 0, 200, 60, 0, 0, 0, 0);
5763 else
5764 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE,
5765 0, 0, 200, 60, 0, 0, 0, 0);
5766 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5768 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5769 ok (result == FALSE, "Can undo after window creation.\n");
5770 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5771 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5772 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5773 ok (result == FALSE, "Can redo after window creation.\n");
5774 result = SendMessage(hwnd, EM_REDO, 0, 0);
5775 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5777 /* Test the effect of arrows keys during typing on undo transactions*/
5778 simulate_typing_characters(hwnd, "one two three");
5779 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5780 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5781 simulate_typing_characters(hwnd, " four five six");
5783 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5784 ok (result == FALSE, "Can redo before anything is undone.\n");
5785 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5786 ok (result == TRUE, "Cannot undo typed characters.\n");
5787 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5788 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5789 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5790 ok (result == TRUE, "Cannot redo after undo.\n");
5791 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5792 result = strcmp(buffer, "one two three");
5793 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5795 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5796 ok (result == TRUE, "Cannot undo typed characters.\n");
5797 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5798 ok (result == TRUE, "Failed to undo typed characters.\n");
5799 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5800 result = strcmp(buffer, "");
5801 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5803 /* Test the effect of focus changes during typing on undo transactions*/
5804 simulate_typing_characters(hwnd, "one two three");
5805 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5806 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5807 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5808 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5809 simulate_typing_characters(hwnd, " four five six");
5810 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5811 ok (result == TRUE, "Failed to undo typed characters.\n");
5812 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5813 result = strcmp(buffer, "one two three");
5814 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5816 /* Test the effect of the back key during typing on undo transactions */
5817 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5818 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5819 ok (result == TRUE, "Failed to clear the text.\n");
5820 simulate_typing_characters(hwnd, "one two threa");
5821 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5822 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5823 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5824 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5825 simulate_typing_characters(hwnd, "e four five six");
5826 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5827 ok (result == TRUE, "Failed to undo typed characters.\n");
5828 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5829 result = strcmp(buffer, "");
5830 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5832 /* Test the effect of the delete key during typing on undo transactions */
5833 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5834 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5835 ok(result == TRUE, "Failed to set the text.\n");
5836 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5837 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5838 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5839 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5840 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5841 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5842 ok (result == TRUE, "Failed to undo typed characters.\n");
5843 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5844 result = strcmp(buffer, "acd");
5845 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5846 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5847 ok (result == TRUE, "Failed to undo typed characters.\n");
5848 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5849 result = strcmp(buffer, "abcd");
5850 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5852 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5853 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5854 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5855 ok (result == TRUE, "Failed to clear the text.\n");
5856 simulate_typing_characters(hwnd, "one two three");
5857 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5858 ok (result == 0, "expected %d but got %d\n", 0, result);
5859 simulate_typing_characters(hwnd, " four five six");
5860 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5861 ok (result == TRUE, "Failed to undo typed characters.\n");
5862 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5863 result = strcmp(buffer, "one two three");
5864 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5865 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5866 ok (result == TRUE, "Failed to undo typed characters.\n");
5867 ok (result == TRUE, "Failed to undo typed characters.\n");
5868 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5869 result = strcmp(buffer, "");
5870 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5872 DestroyWindow(hwnd);
5875 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5877 int length;
5879 /* MSDN lied, length is actually the number of bytes. */
5880 length = bytes / sizeof(WCHAR);
5881 switch(code)
5883 case WB_ISDELIMITER:
5884 return text[pos] == 'X';
5885 case WB_LEFT:
5886 case WB_MOVEWORDLEFT:
5887 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5888 return pos-1;
5889 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5890 case WB_LEFTBREAK:
5891 pos--;
5892 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5893 pos--;
5894 return pos;
5895 case WB_RIGHT:
5896 case WB_MOVEWORDRIGHT:
5897 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5898 return pos+1;
5899 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5900 case WB_RIGHTBREAK:
5901 pos++;
5902 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5903 pos++;
5904 return pos;
5905 default:
5906 ok(FALSE, "Unexpected code %d\n", code);
5907 break;
5909 return 0;
5912 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5913 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5915 static void test_word_movement(void)
5917 HWND hwnd;
5918 int result;
5919 int sel_start, sel_end;
5920 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5922 /* multi-line control inserts CR normally */
5923 hwnd = new_richedit(NULL);
5925 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5926 ok (result == TRUE, "Failed to clear the text.\n");
5927 SendMessage(hwnd, EM_SETSEL, 0, 0);
5928 /* |one two three */
5930 SEND_CTRL_RIGHT(hwnd);
5931 /* one |two three */
5932 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5933 ok(sel_start == sel_end, "Selection should be empty\n");
5934 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5936 SEND_CTRL_RIGHT(hwnd);
5937 /* one two |three */
5938 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5939 ok(sel_start == sel_end, "Selection should be empty\n");
5940 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5942 SEND_CTRL_LEFT(hwnd);
5943 /* one |two three */
5944 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5945 ok(sel_start == sel_end, "Selection should be empty\n");
5946 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5948 SEND_CTRL_LEFT(hwnd);
5949 /* |one two three */
5950 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5951 ok(sel_start == sel_end, "Selection should be empty\n");
5952 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5954 SendMessage(hwnd, EM_SETSEL, 8, 8);
5955 /* one two | three */
5956 SEND_CTRL_RIGHT(hwnd);
5957 /* one two |three */
5958 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5959 ok(sel_start == sel_end, "Selection should be empty\n");
5960 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5962 SendMessage(hwnd, EM_SETSEL, 11, 11);
5963 /* one two th|ree */
5964 SEND_CTRL_LEFT(hwnd);
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 == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5970 /* Test with a custom word break procedure that uses X as the delimiter. */
5971 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5972 ok (result == TRUE, "Failed to clear the text.\n");
5973 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5974 /* |one twoXthree */
5975 SEND_CTRL_RIGHT(hwnd);
5976 /* one twoX|three */
5977 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5978 ok(sel_start == sel_end, "Selection should be empty\n");
5979 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5981 DestroyWindow(hwnd);
5983 /* Make sure the behaviour is the same with a unicode richedit window,
5984 * and using unicode functions. */
5985 if (is_win9x)
5987 skip("Cannot test with unicode richedit window\n");
5988 return;
5991 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
5992 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5993 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5995 /* Test with a custom word break procedure that uses X as the delimiter. */
5996 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
5997 ok (result == TRUE, "Failed to clear the text.\n");
5998 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5999 /* |one twoXthree */
6000 SEND_CTRL_RIGHT(hwnd);
6001 /* one twoX|three */
6002 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6003 ok(sel_start == sel_end, "Selection should be empty\n");
6004 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6006 DestroyWindow(hwnd);
6009 static void test_EM_CHARFROMPOS(void)
6011 HWND hwnd;
6012 int result;
6013 RECT rcClient;
6014 POINTL point;
6015 point.x = 0;
6016 point.y = 40;
6018 /* multi-line control inserts CR normally */
6019 hwnd = new_richedit(NULL);
6020 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6021 (LPARAM)"one two three four five six seven\reight");
6023 GetClientRect(hwnd, &rcClient);
6025 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6026 ok(result == 34, "expected character index of 34 but got %d\n", result);
6028 /* Test with points outside the bounds of the richedit control. */
6029 point.x = -1;
6030 point.y = 40;
6031 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6032 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6034 point.x = 1000;
6035 point.y = 0;
6036 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6037 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6039 point.x = 1000;
6040 point.y = 40;
6041 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6042 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6044 point.x = 1000;
6045 point.y = -1;
6046 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6047 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6049 point.x = 1000;
6050 point.y = rcClient.bottom + 1;
6051 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6052 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6054 point.x = 1000;
6055 point.y = rcClient.bottom;
6056 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6057 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6059 DestroyWindow(hwnd);
6062 static void test_word_wrap(void)
6064 HWND hwnd;
6065 POINTL point = {0, 60}; /* This point must be below the first line */
6066 const char *text = "Must be long enough to test line wrapping";
6067 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6068 int res, pos, lines;
6070 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6071 * when specified on window creation and set later. */
6072 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6073 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6074 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6075 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6076 ok(res, "WM_SETTEXT failed.\n");
6077 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6078 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6079 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6080 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6082 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6083 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6084 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6085 DestroyWindow(hwnd);
6087 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6088 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6089 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6091 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6092 ok(res, "WM_SETTEXT failed.\n");
6093 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6094 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6095 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6096 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6098 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6099 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6100 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6101 DestroyWindow(hwnd);
6103 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6104 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6105 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6106 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6107 ok(res, "WM_SETTEXT failed.\n");
6108 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6109 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6111 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6112 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6113 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6114 DestroyWindow(hwnd);
6116 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6117 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6118 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6119 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6120 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6121 ok(res, "WM_SETTEXT failed.\n");
6122 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6123 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6125 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6126 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6127 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6129 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6130 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6131 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6132 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6133 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6135 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6136 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6137 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6138 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6139 DestroyWindow(hwnd);
6141 /* Test to see if wrapping happens with redraw disabled. */
6142 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6143 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6144 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6145 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6146 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6147 ok(res, "EM_REPLACESEL failed.\n");
6148 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6149 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6150 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6151 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6152 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6154 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6155 DestroyWindow(hwnd);
6158 static void test_autoscroll(void)
6160 HWND hwnd = new_richedit(NULL);
6161 int lines, ret, redraw;
6162 POINT pt;
6164 for (redraw = 0; redraw <= 1; redraw++) {
6165 trace("testing with WM_SETREDRAW=%d\n", redraw);
6166 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6167 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6168 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6169 ok(lines == 8, "%d lines instead of 8\n", lines);
6170 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6171 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6172 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6173 ret = GetWindowLong(hwnd, GWL_STYLE);
6174 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6176 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6177 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6178 ok(lines == 1, "%d lines instead of 1\n", lines);
6179 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6180 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6181 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6182 ret = GetWindowLong(hwnd, GWL_STYLE);
6183 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6186 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6187 DestroyWindow(hwnd);
6189 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6190 * auto vertical/horizontal scrolling options. */
6191 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6192 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6193 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6194 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6195 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6196 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6197 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6198 ret = GetWindowLong(hwnd, GWL_STYLE);
6199 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6200 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6201 DestroyWindow(hwnd);
6203 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6204 WS_POPUP|ES_MULTILINE,
6205 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6206 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6207 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6208 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6209 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6210 ret = GetWindowLong(hwnd, GWL_STYLE);
6211 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6212 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6213 DestroyWindow(hwnd);
6217 static void test_format_rect(void)
6219 HWND hwnd;
6220 RECT rc, expected, clientRect;
6221 int n;
6222 DWORD options;
6224 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6225 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6226 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6227 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6229 GetClientRect(hwnd, &clientRect);
6231 expected = clientRect;
6232 expected.left += 1;
6233 expected.right -= 1;
6234 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6235 ok(rc.top == expected.top && rc.left == expected.left &&
6236 rc.bottom == expected.bottom && rc.right == expected.right,
6237 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6238 rc.top, rc.left, rc.bottom, rc.right,
6239 expected.top, expected.left, expected.bottom, expected.right);
6241 for (n = -3; n <= 3; n++)
6243 rc = clientRect;
6244 rc.top += n;
6245 rc.left += n;
6246 rc.bottom -= n;
6247 rc.right -= n;
6248 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6250 expected = rc;
6251 expected.top = max(0, rc.top);
6252 expected.left = max(0, rc.left);
6253 expected.bottom = min(clientRect.bottom, rc.bottom);
6254 expected.right = min(clientRect.right, rc.right);
6255 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6256 ok(rc.top == expected.top && rc.left == expected.left &&
6257 rc.bottom == expected.bottom && rc.right == expected.right,
6258 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6259 n, rc.top, rc.left, rc.bottom, rc.right,
6260 expected.top, expected.left, expected.bottom, expected.right);
6263 rc = clientRect;
6264 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6265 expected = clientRect;
6266 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6267 ok(rc.top == expected.top && rc.left == expected.left &&
6268 rc.bottom == expected.bottom && rc.right == expected.right,
6269 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6270 rc.top, rc.left, rc.bottom, rc.right,
6271 expected.top, expected.left, expected.bottom, expected.right);
6273 /* Adding the selectionbar adds the selectionbar width to the left side. */
6274 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6275 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6276 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6277 expected.left += 8; /* selection bar width */
6278 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6279 ok(rc.top == expected.top && rc.left == expected.left &&
6280 rc.bottom == expected.bottom && rc.right == expected.right,
6281 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6282 rc.top, rc.left, rc.bottom, rc.right,
6283 expected.top, expected.left, expected.bottom, expected.right);
6285 rc = clientRect;
6286 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6287 expected = clientRect;
6288 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6289 ok(rc.top == expected.top && rc.left == expected.left &&
6290 rc.bottom == expected.bottom && rc.right == expected.right,
6291 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6292 rc.top, rc.left, rc.bottom, rc.right,
6293 expected.top, expected.left, expected.bottom, expected.right);
6295 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6296 * even if the left side is already 0. */
6297 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6298 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6299 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6300 expected.left -= 8; /* selection bar width */
6301 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6302 ok(rc.top == expected.top && rc.left == expected.left &&
6303 rc.bottom == expected.bottom && rc.right == expected.right,
6304 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6305 rc.top, rc.left, rc.bottom, rc.right,
6306 expected.top, expected.left, expected.bottom, expected.right);
6308 /* Set the absolute value of the formatting rectangle. */
6309 rc = clientRect;
6310 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6311 expected = clientRect;
6312 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6313 ok(rc.top == expected.top && rc.left == expected.left &&
6314 rc.bottom == expected.bottom && rc.right == expected.right,
6315 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6316 n, rc.top, rc.left, rc.bottom, rc.right,
6317 expected.top, expected.left, expected.bottom, expected.right);
6319 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6320 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6321 * tests show that this isn't true. */
6322 rc.top = 15;
6323 rc.left = 15;
6324 rc.bottom = clientRect.bottom - 15;
6325 rc.right = clientRect.right - 15;
6326 expected = rc;
6327 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6328 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6329 ok(rc.top == expected.top && rc.left == expected.left &&
6330 rc.bottom == expected.bottom && rc.right == expected.right,
6331 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6332 rc.top, rc.left, rc.bottom, rc.right,
6333 expected.top, expected.left, expected.bottom, expected.right);
6335 /* For some reason it does not limit the values to the client rect with
6336 * a WPARAM value of 1. */
6337 rc.top = -15;
6338 rc.left = -15;
6339 rc.bottom = clientRect.bottom + 15;
6340 rc.right = clientRect.right + 15;
6341 expected = rc;
6342 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6343 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6344 ok(rc.top == expected.top && rc.left == expected.left &&
6345 rc.bottom == expected.bottom && rc.right == expected.right,
6346 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6347 rc.top, rc.left, rc.bottom, rc.right,
6348 expected.top, expected.left, expected.bottom, expected.right);
6350 DestroyWindow(hwnd);
6352 /* The extended window style affects the formatting rectangle. */
6353 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6354 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6355 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6356 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6358 GetClientRect(hwnd, &clientRect);
6360 expected = clientRect;
6361 expected.left += 1;
6362 expected.top += 1;
6363 expected.right -= 1;
6364 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6365 ok(rc.top == expected.top && rc.left == expected.left &&
6366 rc.bottom == expected.bottom && rc.right == expected.right,
6367 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6368 rc.top, rc.left, rc.bottom, rc.right,
6369 expected.top, expected.left, expected.bottom, expected.right);
6371 rc = clientRect;
6372 rc.top += 5;
6373 rc.left += 5;
6374 rc.bottom -= 5;
6375 rc.right -= 5;
6376 expected = rc;
6377 expected.top -= 1;
6378 expected.left -= 1;
6379 expected.right += 1;
6380 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6381 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6382 ok(rc.top == expected.top && rc.left == expected.left &&
6383 rc.bottom == expected.bottom && rc.right == expected.right,
6384 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6385 rc.top, rc.left, rc.bottom, rc.right,
6386 expected.top, expected.left, expected.bottom, expected.right);
6388 DestroyWindow(hwnd);
6391 static void test_WM_GETDLGCODE(void)
6393 HWND hwnd;
6394 UINT res, expected;
6395 MSG msg;
6397 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6399 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6400 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6401 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6402 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6403 msg.hwnd = hwnd;
6404 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6405 expected = expected | DLGC_WANTMESSAGE;
6406 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6407 res, expected);
6408 DestroyWindow(hwnd);
6410 msg.message = WM_KEYDOWN;
6411 msg.wParam = VK_RETURN;
6412 msg.lParam = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) | 0x0001;
6413 msg.pt.x = 0;
6414 msg.pt.y = 0;
6415 msg.time = GetTickCount();
6417 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6418 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6419 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6420 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6421 msg.hwnd = hwnd;
6422 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6423 expected = expected | DLGC_WANTMESSAGE;
6424 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6425 res, expected);
6426 DestroyWindow(hwnd);
6428 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6429 ES_MULTILINE|WS_POPUP,
6430 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6431 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6432 msg.hwnd = hwnd;
6433 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6434 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6435 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6436 res, expected);
6437 DestroyWindow(hwnd);
6439 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6440 ES_WANTRETURN|WS_POPUP,
6441 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6442 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6443 msg.hwnd = hwnd;
6444 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6445 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6446 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6447 res, expected);
6448 DestroyWindow(hwnd);
6450 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6451 WS_POPUP,
6452 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6453 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6454 msg.hwnd = hwnd;
6455 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6456 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6457 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6458 res, expected);
6459 DestroyWindow(hwnd);
6461 msg.wParam = VK_TAB;
6462 msg.lParam = MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) | 0x0001;
6464 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6465 ES_MULTILINE|WS_POPUP,
6466 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6467 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6468 msg.hwnd = hwnd;
6469 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6470 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6471 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6472 res, expected);
6473 DestroyWindow(hwnd);
6475 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6476 WS_POPUP,
6477 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6478 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6479 msg.hwnd = hwnd;
6480 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6481 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6482 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6483 res, expected);
6484 DestroyWindow(hwnd);
6486 hold_key(VK_CONTROL);
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 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 release_key(VK_CONTROL);
6512 msg.wParam = 'a';
6513 msg.lParam = MapVirtualKey('a', MAPVK_VK_TO_VSC) | 0x0001;
6515 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6516 ES_MULTILINE|WS_POPUP,
6517 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6518 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6519 msg.hwnd = hwnd;
6520 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6521 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6522 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6523 res, expected);
6524 DestroyWindow(hwnd);
6526 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6527 WS_POPUP,
6528 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6529 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6530 msg.hwnd = hwnd;
6531 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6532 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6533 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6534 res, expected);
6535 DestroyWindow(hwnd);
6537 msg.message = WM_CHAR;
6539 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6540 ES_MULTILINE|WS_POPUP,
6541 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6542 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6543 msg.hwnd = hwnd;
6544 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6545 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6546 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6547 res, expected);
6548 DestroyWindow(hwnd);
6550 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6551 WS_POPUP,
6552 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6553 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6554 msg.hwnd = hwnd;
6555 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6556 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6557 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6558 res, expected);
6559 DestroyWindow(hwnd);
6562 static void test_zoom(void)
6564 HWND hwnd;
6565 UINT ret;
6566 RECT rc;
6567 POINT pt;
6568 int numerator, denominator;
6570 hwnd = new_richedit(NULL);
6571 GetClientRect(hwnd, &rc);
6572 pt.x = (rc.right - rc.left) / 2;
6573 pt.y = (rc.bottom - rc.top) / 2;
6574 ClientToScreen(hwnd, &pt);
6576 /* Test initial zoom value */
6577 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6578 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6579 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6580 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6582 /* test scroll wheel */
6583 hold_key(VK_CONTROL);
6584 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6585 MAKELPARAM(pt.x, pt.y));
6586 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6587 release_key(VK_CONTROL);
6589 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6590 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6591 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6592 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6594 /* Test how much the mouse wheel can zoom in and out. */
6595 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)490, (LPARAM)100);
6596 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6598 hold_key(VK_CONTROL);
6599 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6600 MAKELPARAM(pt.x, pt.y));
6601 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6602 release_key(VK_CONTROL);
6604 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6605 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6606 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6607 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6609 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)491, (LPARAM)100);
6610 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6612 hold_key(VK_CONTROL);
6613 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6614 MAKELPARAM(pt.x, pt.y));
6615 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6616 release_key(VK_CONTROL);
6618 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6619 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6620 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6621 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6623 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)20, (LPARAM)100);
6624 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6626 hold_key(VK_CONTROL);
6627 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6628 MAKELPARAM(pt.x, pt.y));
6629 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6630 release_key(VK_CONTROL);
6632 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6633 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6634 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6635 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6637 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)19, (LPARAM)100);
6638 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6640 hold_key(VK_CONTROL);
6641 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6642 MAKELPARAM(pt.x, pt.y));
6643 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6644 release_key(VK_CONTROL);
6646 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6647 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6648 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6649 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6651 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6652 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)50, (LPARAM)13);
6653 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6655 hold_key(VK_CONTROL);
6656 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6657 MAKELPARAM(pt.x, pt.y));
6658 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6659 release_key(VK_CONTROL);
6661 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6662 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6663 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6664 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6666 /* Test bounds checking on EM_SETZOOM */
6667 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)127);
6668 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6670 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)127, (LPARAM)2);
6671 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6673 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)128);
6674 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6676 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6677 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6678 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6679 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6681 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)128, (LPARAM)2);
6682 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6684 /* See if negative numbers are accepted. */
6685 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)-100, (LPARAM)-100);
6686 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6688 /* See if negative numbers are accepted. */
6689 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)100);
6690 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6692 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6693 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6694 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6695 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6697 /* Reset the zoom value */
6698 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)0);
6699 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6701 DestroyWindow(hwnd);
6704 struct dialog_mode_messages
6706 int wm_getdefid, wm_close, wm_nextdlgctl;
6709 static struct dialog_mode_messages dm_messages;
6711 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6712 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6713 "got %d\n", wmclose, dm_messages.wm_close); \
6714 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6715 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6716 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6717 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6719 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6721 switch (iMsg)
6723 case DM_GETDEFID:
6724 dm_messages.wm_getdefid++;
6725 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6726 case WM_NEXTDLGCTL:
6727 dm_messages.wm_nextdlgctl++;
6728 break;
6729 case WM_CLOSE:
6730 dm_messages.wm_close++;
6731 break;
6734 return DefWindowProc(hwnd, iMsg, wParam, lParam);
6737 static void test_dialogmode(void)
6739 HWND hwRichEdit, hwParent, hwButton;
6740 MSG msg= {0};
6741 int lcount, r;
6742 WNDCLASSA cls;
6744 cls.style = 0;
6745 cls.lpfnWndProc = dialog_mode_wnd_proc;
6746 cls.cbClsExtra = 0;
6747 cls.cbWndExtra = 0;
6748 cls.hInstance = GetModuleHandleA(0);
6749 cls.hIcon = 0;
6750 cls.hCursor = LoadCursorA(0, IDC_ARROW);
6751 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6752 cls.lpszMenuName = NULL;
6753 cls.lpszClassName = "DialogModeParentClass";
6754 if(!RegisterClassA(&cls)) assert(0);
6756 hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6757 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6759 /* Test richedit(ES_MULTILINE) */
6761 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6763 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6764 ok(0 == r, "expected 0, got %d\n", r);
6765 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6766 ok(2 == lcount, "expected 2, got %d\n", lcount);
6768 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6769 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6771 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6772 ok(0 == r, "expected 0, got %d\n", r);
6773 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6774 ok(3 == lcount, "expected 3, got %d\n", lcount);
6776 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6777 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6778 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6779 ok(0 == r, "expected 0, got %d\n", r);
6780 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6781 ok(3 == lcount, "expected 3, got %d\n", lcount);
6783 DestroyWindow(hwRichEdit);
6785 /* Test standalone richedit(ES_MULTILINE) */
6787 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6789 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6790 ok(0 == r, "expected 0, got %d\n", r);
6791 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6792 ok(2 == lcount, "expected 2, got %d\n", lcount);
6794 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6795 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6797 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6798 ok(0 == r, "expected 0, got %d\n", r);
6799 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6800 ok(2 == lcount, "expected 2, got %d\n", lcount);
6802 DestroyWindow(hwRichEdit);
6804 /* Check a destination for messages */
6806 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6808 SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6809 SetParent( hwRichEdit, NULL);
6811 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6812 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6814 memset(&dm_messages, 0, sizeof(dm_messages));
6815 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6816 ok(0 == r, "expected 0, got %d\n", r);
6817 test_dm_messages(0, 1, 0);
6819 memset(&dm_messages, 0, sizeof(dm_messages));
6820 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6821 ok(0 == r, "expected 0, got %d\n", r);
6822 test_dm_messages(0, 0, 1);
6824 DestroyWindow(hwRichEdit);
6826 /* Check messages from richedit(ES_MULTILINE) */
6828 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6830 memset(&dm_messages, 0, sizeof(dm_messages));
6831 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6832 ok(0 == r, "expected 0, got %d\n", r);
6833 test_dm_messages(0, 0, 0);
6835 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6836 ok(2 == lcount, "expected 2, got %d\n", lcount);
6838 memset(&dm_messages, 0, sizeof(dm_messages));
6839 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6840 ok(0 == r, "expected 0, got %d\n", r);
6841 test_dm_messages(0, 0, 0);
6843 memset(&dm_messages, 0, sizeof(dm_messages));
6844 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6845 ok(0 == r, "expected 0, got %d\n", r);
6846 test_dm_messages(0, 0, 0);
6848 memset(&dm_messages, 0, sizeof(dm_messages));
6849 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6850 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6851 test_dm_messages(0, 0, 0);
6853 memset(&dm_messages, 0, sizeof(dm_messages));
6854 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6855 ok(0 == r, "expected 0, got %d\n", r);
6856 test_dm_messages(0, 1, 0);
6858 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6859 ok(2 == lcount, "expected 2, got %d\n", lcount);
6861 memset(&dm_messages, 0, sizeof(dm_messages));
6862 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6863 ok(0 == r, "expected 0, got %d\n", r);
6864 test_dm_messages(0, 0, 0);
6866 memset(&dm_messages, 0, sizeof(dm_messages));
6867 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6868 ok(0 == r, "expected 0, got %d\n", r);
6869 test_dm_messages(0, 0, 1);
6871 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6872 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6873 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6875 memset(&dm_messages, 0, sizeof(dm_messages));
6876 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6877 ok(0 == r, "expected 0, got %d\n", r);
6878 test_dm_messages(0, 1, 1);
6880 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6881 ok(2 == lcount, "expected 2, got %d\n", lcount);
6883 DestroyWindow(hwButton);
6884 DestroyWindow(hwRichEdit);
6886 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
6888 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, 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, 0, 0);
6918 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6919 ok(3 == lcount, "expected 3, 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, 0, 0);
6940 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6941 ok(4 == lcount, "expected 4, got %d\n", lcount);
6943 DestroyWindow(hwButton);
6944 DestroyWindow(hwRichEdit);
6946 /* Check messages from richedit(0) */
6948 hwRichEdit = new_window(RICHEDIT_CLASS, 0, 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 memset(&dm_messages, 0, sizeof(dm_messages));
6956 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6957 ok(0 == r, "expected 0, got %d\n", r);
6958 test_dm_messages(0, 0, 0);
6960 memset(&dm_messages, 0, sizeof(dm_messages));
6961 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6962 ok(0 == r, "expected 0, got %d\n", r);
6963 test_dm_messages(0, 0, 0);
6965 memset(&dm_messages, 0, sizeof(dm_messages));
6966 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6967 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
6968 test_dm_messages(0, 0, 0);
6970 memset(&dm_messages, 0, sizeof(dm_messages));
6971 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6972 ok(0 == r, "expected 0, got %d\n", r);
6973 test_dm_messages(0, 1, 0);
6975 memset(&dm_messages, 0, sizeof(dm_messages));
6976 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6977 ok(0 == r, "expected 0, got %d\n", r);
6978 test_dm_messages(0, 0, 0);
6980 memset(&dm_messages, 0, sizeof(dm_messages));
6981 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6982 ok(0 == r, "expected 0, got %d\n", r);
6983 test_dm_messages(0, 0, 1);
6985 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6986 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6987 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6989 memset(&dm_messages, 0, sizeof(dm_messages));
6990 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6991 ok(0 == r, "expected 0, got %d\n", r);
6992 test_dm_messages(0, 1, 1);
6994 DestroyWindow(hwRichEdit);
6996 /* Check messages from richedit(ES_WANTRETURN) */
6998 hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7000 memset(&dm_messages, 0, sizeof(dm_messages));
7001 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7002 ok(0 == r, "expected 0, got %d\n", r);
7003 test_dm_messages(0, 0, 0);
7005 memset(&dm_messages, 0, sizeof(dm_messages));
7006 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7007 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7008 test_dm_messages(0, 0, 0);
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 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7016 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7017 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7019 memset(&dm_messages, 0, sizeof(dm_messages));
7020 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7021 ok(0 == r, "expected 0, got %d\n", r);
7022 test_dm_messages(0, 0, 0);
7024 DestroyWindow(hwRichEdit);
7025 DestroyWindow(hwParent);
7028 START_TEST( editor )
7030 /* Must explicitly LoadLibrary(). The test has no references to functions in
7031 * RICHED20.DLL, so the linker doesn't actually link to it. */
7032 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
7033 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7035 is_win9x = GetVersion() & 0x80000000;
7037 test_WM_CHAR();
7038 test_EM_FINDTEXT();
7039 test_EM_GETLINE();
7040 test_EM_POSFROMCHAR();
7041 test_EM_SCROLLCARET();
7042 test_EM_SCROLL();
7043 test_scrollbar_visibility();
7044 test_WM_SETTEXT();
7045 test_EM_LINELENGTH();
7046 test_EM_SETCHARFORMAT();
7047 test_EM_SETTEXTMODE();
7048 test_TM_PLAINTEXT();
7049 test_EM_SETOPTIONS();
7050 test_WM_GETTEXT();
7051 test_EM_GETTEXTRANGE();
7052 test_EM_GETSELTEXT();
7053 test_EM_SETUNDOLIMIT();
7054 test_ES_PASSWORD();
7055 test_EM_SETTEXTEX();
7056 test_EM_LIMITTEXT();
7057 test_EM_EXLIMITTEXT();
7058 test_EM_GETLIMITTEXT();
7059 test_WM_SETFONT();
7060 test_EM_GETMODIFY();
7061 test_EM_EXSETSEL();
7062 test_WM_PASTE();
7063 test_EM_STREAMIN();
7064 test_EM_STREAMOUT();
7065 test_EM_STREAMOUT_FONTTBL();
7066 test_EM_StreamIn_Undo();
7067 test_EM_FORMATRANGE();
7068 test_unicode_conversions();
7069 test_EM_GETTEXTLENGTHEX();
7070 test_EM_REPLACESEL(1);
7071 test_EM_REPLACESEL(0);
7072 test_WM_NOTIFY();
7073 test_EM_AUTOURLDETECT();
7074 test_eventMask();
7075 test_undo_coalescing();
7076 test_word_movement();
7077 test_EM_CHARFROMPOS();
7078 test_SETPARAFORMAT();
7079 test_word_wrap();
7080 test_autoscroll();
7081 test_format_rect();
7082 test_WM_GETDLGCODE();
7083 test_zoom();
7084 test_dialogmode();
7086 /* Set the environment variable WINETEST_RICHED20 to keep windows
7087 * responsive and open for 30 seconds. This is useful for debugging.
7089 if (getenv( "WINETEST_RICHED20" )) {
7090 keep_responsive(30);
7093 OleFlushClipboard();
7094 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());