riched20: Fixed some lParams with keycodes (Coverity).
[wine/multimedia.git] / dlls / riched20 / tests / editor.c
blobc0ad42fd45745dda2b37b86d81f0c801c3b504ac
1 /*
2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
36 #define ID_RICHEDITTESTDBUTTON 0x123
38 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
40 #define ok_w3(format, szString1, szString2, szString3) \
41 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
42 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
43 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
44 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
45 format, string1, string2, string3);
47 static HMODULE hmoduleRichEdit;
49 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
50 HWND hwnd;
51 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
52 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
53 hmoduleRichEdit, NULL);
54 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
55 return hwnd;
58 static HWND new_richedit(HWND parent) {
59 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
62 /* Keeps the window reponsive for the deley_time in seconds.
63 * This is useful for debugging a test to see what is happening. */
64 static void keep_responsive(time_t delay_time)
66 MSG msg;
67 time_t end;
69 /* The message pump uses PeekMessage() to empty the queue and then
70 * sleeps for 50ms before retrying the queue. */
71 end = time(NULL) + delay_time;
72 while (time(NULL) < end) {
73 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
74 TranslateMessage(&msg);
75 DispatchMessage(&msg);
76 } else {
77 Sleep(50);
82 static void processPendingMessages(void)
84 MSG msg;
85 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
86 TranslateMessage(&msg);
87 DispatchMessage(&msg);
91 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
93 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
94 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
95 SetFocus(hwnd);
96 keybd_event(mod_vk, mod_scan_code, 0, 0);
97 keybd_event(vk, scan_code, 0, 0);
98 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
99 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
100 processPendingMessages();
103 static void simulate_typing_characters(HWND hwnd, const char* szChars)
105 int ret;
107 while (*szChars != '\0') {
108 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
109 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
110 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
111 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
112 szChars++;
116 static BOOL hold_key(int vk)
118 BYTE key_state[256];
119 BOOL result;
121 result = GetKeyboardState(key_state);
122 ok(result, "GetKeyboardState failed.\n");
123 if (!result) return FALSE;
124 key_state[vk] |= 0x80;
125 result = SetKeyboardState(key_state);
126 ok(result, "SetKeyboardState failed.\n");
127 return result != 0;
130 static BOOL release_key(int vk)
132 BYTE key_state[256];
133 BOOL result;
135 result = GetKeyboardState(key_state);
136 ok(result, "GetKeyboardState failed.\n");
137 if (!result) return FALSE;
138 key_state[vk] &= ~0x80;
139 result = SetKeyboardState(key_state);
140 ok(result, "SetKeyboardState failed.\n");
141 return result != 0;
144 static const char haystack[] = "WINEWine wineWine wine WineWine";
145 /* ^0 ^10 ^20 ^30 */
147 struct find_s {
148 int start;
149 int end;
150 const char *needle;
151 int flags;
152 int expected_loc;
156 static struct find_s find_tests[] = {
157 /* Find in empty text */
158 {0, -1, "foo", FR_DOWN, -1},
159 {0, -1, "foo", 0, -1},
160 {0, -1, "", FR_DOWN, -1},
161 {20, 5, "foo", FR_DOWN, -1},
162 {5, 20, "foo", FR_DOWN, -1}
165 static struct find_s find_tests2[] = {
166 /* No-result find */
167 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
168 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
170 /* Subsequent finds */
171 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
172 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
173 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
174 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
176 /* Find backwards */
177 {19, 20, "Wine", FR_MATCHCASE, 13},
178 {10, 20, "Wine", FR_MATCHCASE, 4},
179 {20, 10, "Wine", FR_MATCHCASE, 13},
181 /* Case-insensitive */
182 {1, 31, "wInE", FR_DOWN, 4},
183 {1, 31, "Wine", FR_DOWN, 4},
185 /* High-to-low ranges */
186 {20, 5, "Wine", FR_DOWN, -1},
187 {2, 1, "Wine", FR_DOWN, -1},
188 {30, 29, "Wine", FR_DOWN, -1},
189 {20, 5, "Wine", 0, 13},
191 /* Find nothing */
192 {5, 10, "", FR_DOWN, -1},
193 {10, 5, "", FR_DOWN, -1},
194 {0, -1, "", FR_DOWN, -1},
195 {10, 5, "", 0, -1},
197 /* Whole-word search */
198 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
199 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
200 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
201 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
202 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
203 {11, -1, "winewine", FR_WHOLEWORD, 0},
204 {31, -1, "winewine", FR_WHOLEWORD, 23},
206 /* Bad ranges */
207 {5, 200, "XXX", FR_DOWN, -1},
208 {-20, 20, "Wine", FR_DOWN, -1},
209 {-20, 20, "Wine", FR_DOWN, -1},
210 {-15, -20, "Wine", FR_DOWN, -1},
211 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
213 /* Check the case noted in bug 4479 where matches at end aren't recognized */
214 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
215 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
216 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
217 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
218 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
220 /* The backwards case of bug 4479; bounds look right
221 * Fails because backward find is wrong */
222 {19, 20, "WINE", FR_MATCHCASE, 0},
223 {0, 20, "WINE", FR_MATCHCASE, -1},
225 {0, -1, "wineWine wine", 0, -1},
228 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
229 int findloc;
230 FINDTEXT ft;
231 memset(&ft, 0, sizeof(ft));
232 ft.chrg.cpMin = f->start;
233 ft.chrg.cpMax = f->end;
234 ft.lpstrText = f->needle;
235 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
236 ok(findloc == f->expected_loc,
237 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
238 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
241 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
242 int id) {
243 int findloc;
244 FINDTEXTEX ft;
245 int expected_end_loc;
247 memset(&ft, 0, sizeof(ft));
248 ft.chrg.cpMin = f->start;
249 ft.chrg.cpMax = f->end;
250 ft.lpstrText = f->needle;
251 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
252 ok(findloc == f->expected_loc,
253 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
254 name, id, f->needle, f->start, f->end, f->flags, findloc);
255 ok(ft.chrgText.cpMin == f->expected_loc,
256 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
257 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
258 expected_end_loc = ((f->expected_loc == -1) ? -1
259 : f->expected_loc + strlen(f->needle));
260 ok(ft.chrgText.cpMax == expected_end_loc,
261 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
262 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
265 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
266 int num_tests)
268 int i;
270 for (i = 0; i < num_tests; i++) {
271 check_EM_FINDTEXT(hwnd, name, &find[i], i);
272 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
276 static void test_EM_FINDTEXT(void)
278 HWND hwndRichEdit = new_richedit(NULL);
279 CHARFORMAT2 cf2;
281 /* Empty rich edit control */
282 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
283 sizeof(find_tests)/sizeof(struct find_s));
285 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
287 /* Haystack text */
288 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
289 sizeof(find_tests2)/sizeof(struct find_s));
291 /* Setting a format on an arbitrary range should have no effect in search
292 results. This tests correct offset reporting across runs. */
293 cf2.cbSize = sizeof(CHARFORMAT2);
294 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
295 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
296 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
297 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
298 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
300 /* Haystack text, again */
301 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
302 sizeof(find_tests2)/sizeof(struct find_s));
304 /* Yet another range */
305 cf2.dwMask = CFM_BOLD | cf2.dwMask;
306 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
307 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
308 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
310 /* Haystack text, again */
311 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
312 sizeof(find_tests2)/sizeof(struct find_s));
314 DestroyWindow(hwndRichEdit);
317 static const struct getline_s {
318 int line;
319 size_t buffer_len;
320 const char *text;
321 } gl[] = {
322 {0, 10, "foo bar\r"},
323 {1, 10, "\r"},
324 {2, 10, "bar\r"},
325 {3, 10, "\r"},
327 /* Buffer smaller than line length */
328 {0, 2, "foo bar\r"},
329 {0, 1, "foo bar\r"},
330 {0, 0, "foo bar\r"}
333 static void test_EM_GETLINE(void)
335 int i;
336 HWND hwndRichEdit = new_richedit(NULL);
337 static const int nBuf = 1024;
338 char dest[1024], origdest[1024];
339 const char text[] = "foo bar\n"
340 "\n"
341 "bar\n";
343 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
345 memset(origdest, 0xBB, nBuf);
346 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
348 int nCopied;
349 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
350 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
351 memset(dest, 0xBB, nBuf);
352 *(WORD *) dest = gl[i].buffer_len;
354 /* EM_GETLINE appends a "\r\0" to the end of the line
355 * nCopied counts up to and including the '\r' */
356 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
357 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
358 expected_nCopied);
359 /* two special cases since a parameter is passed via dest */
360 if (gl[i].buffer_len == 0)
361 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
362 "buffer_len=0\n");
363 else if (gl[i].buffer_len == 1)
364 ok(dest[0] == gl[i].text[0] && !dest[1] &&
365 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
366 else
368 /* Prepare hex strings of buffers to dump on failure. */
369 char expectedbuf[1024];
370 char resultbuf[1024];
371 int j;
372 resultbuf[0] = '\0';
373 for (j = 0; j < 32; j++)
374 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
375 expectedbuf[0] = '\0';
376 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
377 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
378 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
379 sprintf(expectedbuf+strlen(expectedbuf), "??");
380 for (; j < 32; j++) /* Bytes after declared buffer size */
381 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
383 /* Test the part of the buffer that is expected to be written according
384 * to the MSDN documentation fo EM_GETLINE, which does not state that
385 * a NULL terminating character will be added unless no text is copied.
387 * Windows NT does not append a NULL terminating character, but
388 * Windows 2000 and up do append a NULL terminating character if there
389 * is space in the buffer. The test will ignore this difference. */
390 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
391 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
392 i, expected_bytes_written, expectedbuf, resultbuf);
393 /* Test the part of the buffer after the declared length to make sure
394 * there are no buffer overruns. */
395 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
396 nBuf - gl[i].buffer_len),
397 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
398 i, expected_bytes_written, expectedbuf, resultbuf);
402 DestroyWindow(hwndRichEdit);
405 static void test_EM_LINELENGTH(void)
407 HWND hwndRichEdit = new_richedit(NULL);
408 const char * text =
409 "richedit1\r"
410 "richedit1\n"
411 "richedit1\r\n"
412 "richedit1";
413 int offset_test[10][2] = {
414 {0, 9},
415 {5, 9},
416 {10, 9},
417 {15, 9},
418 {20, 9},
419 {25, 9},
420 {30, 9},
421 {35, 9},
422 {40, 0},
423 {45, 0},
425 int i;
426 LRESULT result;
428 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
430 for (i = 0; i < 10; i++) {
431 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
432 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
433 offset_test[i][0], result, offset_test[i][1]);
436 DestroyWindow(hwndRichEdit);
439 static int get_scroll_pos_y(HWND hwnd)
441 POINT p = {-1, -1};
442 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
443 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
444 return p.y;
447 static void move_cursor(HWND hwnd, LONG charindex)
449 CHARRANGE cr;
450 cr.cpMax = charindex;
451 cr.cpMin = charindex;
452 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
455 static void line_scroll(HWND hwnd, int amount)
457 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
460 static void test_EM_SCROLLCARET(void)
462 int prevY, curY;
463 const char text[] = "aa\n"
464 "this is a long line of text that should be longer than the "
465 "control's width\n"
466 "cc\n"
467 "dd\n"
468 "ee\n"
469 "ff\n"
470 "gg\n"
471 "hh\n";
472 /* The richedit window height needs to be large enough vertically to fit in
473 * more than two lines of text, so the new_richedit function can't be used
474 * since a height of 60 was not large enough on some systems.
476 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
477 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
478 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
479 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
481 /* Can't verify this */
482 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
484 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
486 /* Caret above visible window */
487 line_scroll(hwndRichEdit, 3);
488 prevY = get_scroll_pos_y(hwndRichEdit);
489 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
490 curY = get_scroll_pos_y(hwndRichEdit);
491 ok(prevY != curY, "%d == %d\n", prevY, curY);
493 /* Caret below visible window */
494 move_cursor(hwndRichEdit, sizeof(text) - 1);
495 line_scroll(hwndRichEdit, -3);
496 prevY = get_scroll_pos_y(hwndRichEdit);
497 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
498 curY = get_scroll_pos_y(hwndRichEdit);
499 ok(prevY != curY, "%d == %d\n", prevY, curY);
501 /* Caret in visible window */
502 move_cursor(hwndRichEdit, sizeof(text) - 2);
503 prevY = get_scroll_pos_y(hwndRichEdit);
504 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
505 curY = get_scroll_pos_y(hwndRichEdit);
506 ok(prevY == curY, "%d != %d\n", prevY, curY);
508 /* Caret still in visible window */
509 line_scroll(hwndRichEdit, -1);
510 prevY = get_scroll_pos_y(hwndRichEdit);
511 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
512 curY = get_scroll_pos_y(hwndRichEdit);
513 ok(prevY == curY, "%d != %d\n", prevY, curY);
515 DestroyWindow(hwndRichEdit);
518 static void test_EM_POSFROMCHAR(void)
520 HWND hwndRichEdit = new_richedit(NULL);
521 int i, expected;
522 LRESULT result;
523 unsigned int height = 0;
524 int xpos = 0;
525 POINTL pt;
526 LOCALESIGNATURE sig;
527 BOOL rtl;
528 static const char text[] = "aa\n"
529 "this is a long line of text that should be longer than the "
530 "control's width\n"
531 "cc\n"
532 "dd\n"
533 "ee\n"
534 "ff\n"
535 "gg\n"
536 "hh\n";
538 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
539 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
540 (sig.lsUsb[3] & 0x08000000) != 0);
542 /* Fill the control to lines to ensure that most of them are offscreen */
543 for (i = 0; i < 50; i++)
545 /* Do not modify the string; it is exactly 16 characters long. */
546 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
547 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
551 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
552 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
553 Richedit 3.0 accepts either of the above API conventions.
556 /* Testing Richedit 2.0 API format */
558 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
559 Since all lines are identical and drawn with the same font,
560 they should have the same height... right?
562 for (i = 0; i < 50; i++)
564 /* All the lines are 16 characters long */
565 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
566 if (i == 0)
568 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
569 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
570 xpos = LOWORD(result);
572 else if (i == 1)
574 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
575 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
576 height = HIWORD(result);
578 else
580 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
581 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
585 /* Testing position at end of text */
586 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
587 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
588 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
590 /* Testing position way past end of text */
591 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
592 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
593 expected = (rtl ? 8 : 1);
594 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
596 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
597 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
598 for (i = 0; i < 50; i++)
600 /* All the lines are 16 characters long */
601 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
602 ok((signed short)(HIWORD(result)) == (i - 1) * height,
603 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
604 (signed short)(HIWORD(result)), (i - 1) * height);
605 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
608 /* Testing position at end of text */
609 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
610 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
611 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
613 /* Testing position way past end of text */
614 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
615 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
616 expected = (rtl ? 8 : 1);
617 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
619 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
620 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
621 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
623 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
624 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
625 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
626 xpos = LOWORD(result);
628 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
629 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
630 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
631 ok((signed short)(LOWORD(result)) < xpos,
632 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
633 (signed short)(LOWORD(result)), xpos);
634 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
636 /* Test around end of text that doesn't end in a newline. */
637 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
638 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
639 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
640 ok(pt.x > 1, "pt.x = %d\n", pt.x);
641 xpos = pt.x;
642 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
643 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
644 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
645 xpos = (rtl ? pt.x + 7 : pt.x);
646 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
647 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
648 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
650 /* Try a negative position. */
651 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
652 ok(pt.x == 1, "pt.x = %d\n", pt.x);
654 DestroyWindow(hwndRichEdit);
657 static void test_EM_SETCHARFORMAT(void)
659 HWND hwndRichEdit = new_richedit(NULL);
660 CHARFORMAT2 cf2;
661 int rc = 0;
662 int tested_effects[] = {
663 CFE_BOLD,
664 CFE_ITALIC,
665 CFE_UNDERLINE,
666 CFE_STRIKEOUT,
667 CFE_PROTECTED,
668 CFE_LINK,
669 CFE_SUBSCRIPT,
670 CFE_SUPERSCRIPT,
673 int i;
674 CHARRANGE cr;
675 LOCALESIGNATURE sig;
676 BOOL rtl;
678 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
679 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
680 (sig.lsUsb[3] & 0x08000000) != 0);
682 /* Invalid flags, CHARFORMAT2 structure blanked out */
683 memset(&cf2, 0, sizeof(cf2));
684 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
685 (LPARAM) &cf2);
686 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
688 /* A valid flag, CHARFORMAT2 structure blanked out */
689 memset(&cf2, 0, sizeof(cf2));
690 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
691 (LPARAM) &cf2);
692 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
694 /* A valid flag, CHARFORMAT2 structure blanked out */
695 memset(&cf2, 0, sizeof(cf2));
696 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
697 (LPARAM) &cf2);
698 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
700 /* A valid flag, CHARFORMAT2 structure blanked out */
701 memset(&cf2, 0, sizeof(cf2));
702 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
703 (LPARAM) &cf2);
704 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
706 /* A valid flag, CHARFORMAT2 structure blanked out */
707 memset(&cf2, 0, sizeof(cf2));
708 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
709 (LPARAM) &cf2);
710 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
712 /* Invalid flags, CHARFORMAT2 structure minimally filled */
713 memset(&cf2, 0, sizeof(cf2));
714 cf2.cbSize = sizeof(CHARFORMAT2);
715 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
716 (LPARAM) &cf2);
717 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
718 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
719 ok(rc == FALSE, "Should not be able to undo here.\n");
720 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
722 /* A valid flag, CHARFORMAT2 structure minimally filled */
723 memset(&cf2, 0, sizeof(cf2));
724 cf2.cbSize = sizeof(CHARFORMAT2);
725 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
726 (LPARAM) &cf2);
727 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
728 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
729 ok(rc == FALSE, "Should not be able to undo here.\n");
730 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
732 /* A valid flag, CHARFORMAT2 structure minimally filled */
733 memset(&cf2, 0, sizeof(cf2));
734 cf2.cbSize = sizeof(CHARFORMAT2);
735 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
736 (LPARAM) &cf2);
737 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
738 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
739 ok(rc == FALSE, "Should not be able to undo here.\n");
740 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
742 /* A valid flag, CHARFORMAT2 structure minimally filled */
743 memset(&cf2, 0, sizeof(cf2));
744 cf2.cbSize = sizeof(CHARFORMAT2);
745 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
746 (LPARAM) &cf2);
747 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
748 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
749 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
750 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
752 /* A valid flag, CHARFORMAT2 structure minimally filled */
753 memset(&cf2, 0, sizeof(cf2));
754 cf2.cbSize = sizeof(CHARFORMAT2);
755 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
756 (LPARAM) &cf2);
757 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
758 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
759 ok(rc == TRUE, "Should not be able to undo here.\n");
760 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
762 cf2.cbSize = sizeof(CHARFORMAT2);
763 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
764 (LPARAM) &cf2);
766 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
767 cf2.cbSize = sizeof(CHARFORMAT2);
768 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
769 (LPARAM) &cf2);
770 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
771 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
773 /* wParam==0 is default char format, does not set modify */
774 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
775 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
776 ok(rc == 0, "Text marked as modified, expected not modified!\n");
777 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
778 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
779 if (! rtl)
781 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
782 ok(rc == 0, "Text marked as modified, expected not modified!\n");
784 else
785 skip("RTL language found\n");
787 /* wParam==SCF_SELECTION sets modify if nonempty selection */
788 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
789 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
790 ok(rc == 0, "Text marked as modified, expected not modified!\n");
791 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
792 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
793 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
794 ok(rc == 0, "Text marked as modified, expected not modified!\n");
796 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
797 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
798 ok(rc == 0, "Text marked as modified, expected not modified!\n");
799 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
800 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
801 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
802 ok(rc == 0, "Text marked as modified, expected not modified!\n");
803 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
804 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
805 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
806 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
807 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
809 /* wParam==SCF_ALL sets modify regardless of whether text is present */
810 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
811 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
812 ok(rc == 0, "Text marked as modified, expected not modified!\n");
813 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
814 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
815 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
816 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
818 DestroyWindow(hwndRichEdit);
820 /* EM_GETCHARFORMAT tests */
821 for (i = 0; tested_effects[i]; i++)
823 hwndRichEdit = new_richedit(NULL);
824 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
826 /* Need to set a TrueType font to get consistent CFM_BOLD results */
827 memset(&cf2, 0, sizeof(CHARFORMAT2));
828 cf2.cbSize = sizeof(CHARFORMAT2);
829 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
830 cf2.dwEffects = 0;
831 strcpy(cf2.szFaceName, "Courier New");
832 cf2.wWeight = FW_DONTCARE;
833 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
835 memset(&cf2, 0, sizeof(CHARFORMAT2));
836 cf2.cbSize = sizeof(CHARFORMAT2);
837 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
838 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
839 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
840 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
842 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
843 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
844 ok((cf2.dwEffects & tested_effects[i]) == 0,
845 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
847 memset(&cf2, 0, sizeof(CHARFORMAT2));
848 cf2.cbSize = sizeof(CHARFORMAT2);
849 cf2.dwMask = tested_effects[i];
850 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
851 cf2.dwMask = CFM_SUPERSCRIPT;
852 cf2.dwEffects = tested_effects[i];
853 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
854 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
856 memset(&cf2, 0, sizeof(CHARFORMAT2));
857 cf2.cbSize = sizeof(CHARFORMAT2);
858 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
859 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
860 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
861 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
863 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
864 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
865 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
866 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
868 memset(&cf2, 0, sizeof(CHARFORMAT2));
869 cf2.cbSize = sizeof(CHARFORMAT2);
870 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
871 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
872 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
873 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
875 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
876 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
877 ok((cf2.dwEffects & tested_effects[i]) == 0,
878 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
880 memset(&cf2, 0, sizeof(CHARFORMAT2));
881 cf2.cbSize = sizeof(CHARFORMAT2);
882 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
883 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
884 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
885 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
887 (cf2.dwMask & tested_effects[i]) == 0),
888 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
890 DestroyWindow(hwndRichEdit);
893 for (i = 0; tested_effects[i]; i++)
895 hwndRichEdit = new_richedit(NULL);
896 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
898 /* Need to set a TrueType font to get consistent CFM_BOLD results */
899 memset(&cf2, 0, sizeof(CHARFORMAT2));
900 cf2.cbSize = sizeof(CHARFORMAT2);
901 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
902 cf2.dwEffects = 0;
903 strcpy(cf2.szFaceName, "Courier New");
904 cf2.wWeight = FW_DONTCARE;
905 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
907 memset(&cf2, 0, sizeof(CHARFORMAT2));
908 cf2.cbSize = sizeof(CHARFORMAT2);
909 cf2.dwMask = tested_effects[i];
910 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
911 cf2.dwMask = CFM_SUPERSCRIPT;
912 cf2.dwEffects = tested_effects[i];
913 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
914 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
916 memset(&cf2, 0, sizeof(CHARFORMAT2));
917 cf2.cbSize = sizeof(CHARFORMAT2);
918 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
919 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
920 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
921 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
923 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
924 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
925 ok((cf2.dwEffects & tested_effects[i]) == 0,
926 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
928 memset(&cf2, 0, sizeof(CHARFORMAT2));
929 cf2.cbSize = sizeof(CHARFORMAT2);
930 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
931 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
932 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
933 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
935 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
936 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
937 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
938 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
940 memset(&cf2, 0, sizeof(CHARFORMAT2));
941 cf2.cbSize = sizeof(CHARFORMAT2);
942 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
943 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
944 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
945 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
947 (cf2.dwMask & tested_effects[i]) == 0),
948 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
949 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
950 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
952 DestroyWindow(hwndRichEdit);
955 /* Effects applied on an empty selection should take effect when selection is
956 replaced with text */
957 hwndRichEdit = new_richedit(NULL);
958 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
959 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
961 memset(&cf2, 0, sizeof(CHARFORMAT2));
962 cf2.cbSize = sizeof(CHARFORMAT2);
963 cf2.dwMask = CFM_BOLD;
964 cf2.dwEffects = CFE_BOLD;
965 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
967 /* Selection is now nonempty */
968 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
970 memset(&cf2, 0, sizeof(CHARFORMAT2));
971 cf2.cbSize = sizeof(CHARFORMAT2);
972 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
973 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
975 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
976 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
977 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
978 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
981 /* Set two effects on an empty selection */
982 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
983 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
985 memset(&cf2, 0, sizeof(CHARFORMAT2));
986 cf2.cbSize = sizeof(CHARFORMAT2);
987 cf2.dwMask = CFM_BOLD;
988 cf2.dwEffects = CFE_BOLD;
989 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
990 cf2.dwMask = CFM_ITALIC;
991 cf2.dwEffects = CFE_ITALIC;
992 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
994 /* Selection is now nonempty */
995 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
997 memset(&cf2, 0, sizeof(CHARFORMAT2));
998 cf2.cbSize = sizeof(CHARFORMAT2);
999 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1000 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1002 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1003 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1004 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1005 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1007 /* Setting the (empty) selection to exactly the same place as before should
1008 NOT clear the insertion style! */
1009 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1010 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1012 memset(&cf2, 0, sizeof(CHARFORMAT2));
1013 cf2.cbSize = sizeof(CHARFORMAT2);
1014 cf2.dwMask = CFM_BOLD;
1015 cf2.dwEffects = CFE_BOLD;
1016 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1018 /* Empty selection in same place, insert style should NOT be forgotten here. */
1019 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1021 /* Selection is now nonempty */
1022 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1024 memset(&cf2, 0, sizeof(CHARFORMAT2));
1025 cf2.cbSize = sizeof(CHARFORMAT2);
1026 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1027 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1029 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1030 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1031 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1032 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1034 /* Ditto with EM_EXSETSEL */
1035 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1036 cr.cpMin = 2; cr.cpMax = 2;
1037 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1039 memset(&cf2, 0, sizeof(CHARFORMAT2));
1040 cf2.cbSize = sizeof(CHARFORMAT2);
1041 cf2.dwMask = CFM_BOLD;
1042 cf2.dwEffects = CFE_BOLD;
1043 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1045 /* Empty selection in same place, insert style should NOT be forgotten here. */
1046 cr.cpMin = 2; cr.cpMax = 2;
1047 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1049 /* Selection is now nonempty */
1050 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1052 memset(&cf2, 0, sizeof(CHARFORMAT2));
1053 cf2.cbSize = sizeof(CHARFORMAT2);
1054 cr.cpMin = 2; cr.cpMax = 6;
1055 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1056 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1058 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1059 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1060 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1061 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1063 DestroyWindow(hwndRichEdit);
1066 static void test_EM_SETTEXTMODE(void)
1068 HWND hwndRichEdit = new_richedit(NULL);
1069 CHARFORMAT2 cf2, cf2test;
1070 CHARRANGE cr;
1071 int rc = 0;
1073 /*Attempt to use mutually exclusive modes*/
1074 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT|TM_RICHTEXT, 0);
1075 ok(rc == E_INVALIDARG,
1076 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1078 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1079 /*Insert text into the control*/
1081 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1083 /*Attempt to change the control to plain text mode*/
1084 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1085 ok(rc == E_UNEXPECTED,
1086 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1088 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1089 If rich text is pasted, it should have the same formatting as the rest
1090 of the text in the control*/
1092 /*Italicize the text
1093 *NOTE: If the default text was already italicized, the test will simply
1094 reverse; in other words, it will copy a regular "wine" into a plain
1095 text window that uses an italicized format*/
1096 cf2.cbSize = sizeof(CHARFORMAT2);
1097 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1098 (LPARAM) &cf2);
1100 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1101 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1103 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1104 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1106 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1107 however, SCF_ALL has been implemented*/
1108 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1109 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1111 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1112 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1114 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1116 /*Select the string "wine"*/
1117 cr.cpMin = 0;
1118 cr.cpMax = 4;
1119 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1121 /*Copy the italicized "wine" to the clipboard*/
1122 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1124 /*Reset the formatting to default*/
1125 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1126 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1127 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1129 /*Clear the text in the control*/
1130 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1132 /*Switch to Plain Text Mode*/
1133 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1134 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1136 /*Input "wine" again in normal format*/
1137 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1139 /*Paste the italicized "wine" into the control*/
1140 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1142 /*Select a character from the first "wine" string*/
1143 cr.cpMin = 2;
1144 cr.cpMax = 3;
1145 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1147 /*Retrieve its formatting*/
1148 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1149 (LPARAM) &cf2);
1151 /*Select a character from the second "wine" string*/
1152 cr.cpMin = 5;
1153 cr.cpMax = 6;
1154 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1156 /*Retrieve its formatting*/
1157 cf2test.cbSize = sizeof(CHARFORMAT2);
1158 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1159 (LPARAM) &cf2test);
1161 /*Compare the two formattings*/
1162 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1163 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1164 cf2.dwEffects, cf2test.dwEffects);
1165 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1166 printing "wine" in the current format(normal)
1167 pasting "wine" from the clipboard(italicized)
1168 comparing the two formats(should differ)*/
1170 /*Attempt to switch with text in control*/
1171 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1172 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1174 /*Clear control*/
1175 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1177 /*Switch into Rich Text mode*/
1178 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1179 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1181 /*Print "wine" in normal formatting into the control*/
1182 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1184 /*Paste italicized "wine" into the control*/
1185 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1187 /*Select text from the first "wine" string*/
1188 cr.cpMin = 1;
1189 cr.cpMax = 3;
1190 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1192 /*Retrieve its formatting*/
1193 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1194 (LPARAM) &cf2);
1196 /*Select text from the second "wine" string*/
1197 cr.cpMin = 6;
1198 cr.cpMax = 7;
1199 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1201 /*Retrieve its formatting*/
1202 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1203 (LPARAM) &cf2test);
1205 /*Test that the two formattings are not the same*/
1206 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1207 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1208 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1210 DestroyWindow(hwndRichEdit);
1213 static void test_SETPARAFORMAT(void)
1215 HWND hwndRichEdit = new_richedit(NULL);
1216 PARAFORMAT2 fmt;
1217 HRESULT ret;
1218 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1219 fmt.cbSize = sizeof(PARAFORMAT2);
1220 fmt.dwMask = PFM_ALIGNMENT;
1221 fmt.wAlignment = PFA_LEFT;
1223 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1224 ok(ret != 0, "expected non-zero got %d\n", ret);
1226 fmt.cbSize = sizeof(PARAFORMAT2);
1227 fmt.dwMask = -1;
1228 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1229 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1230 * between richedit different native builds of riched20.dll
1231 * used on different Windows versions. */
1232 ret &= ~PFM_TABLEROWDELIMITER;
1233 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1235 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1236 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1238 DestroyWindow(hwndRichEdit);
1241 static void test_TM_PLAINTEXT(void)
1243 /*Tests plain text properties*/
1245 HWND hwndRichEdit = new_richedit(NULL);
1246 CHARFORMAT2 cf2, cf2test;
1247 CHARRANGE cr;
1248 int rc = 0;
1250 /*Switch to plain text mode*/
1252 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1253 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1255 /*Fill control with text*/
1257 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1259 /*Select some text and bold it*/
1261 cr.cpMin = 10;
1262 cr.cpMax = 20;
1263 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1264 cf2.cbSize = sizeof(CHARFORMAT2);
1265 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1267 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1268 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1270 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1271 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1273 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1274 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1276 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1277 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1279 /*Get the formatting of those characters*/
1281 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1283 /*Get the formatting of some other characters*/
1284 cf2test.cbSize = sizeof(CHARFORMAT2);
1285 cr.cpMin = 21;
1286 cr.cpMax = 30;
1287 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1288 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1290 /*Test that they are the same as plain text allows only one formatting*/
1292 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1293 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1294 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1296 /*Fill the control with a "wine" string, which when inserted will be bold*/
1298 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1300 /*Copy the bolded "wine" string*/
1302 cr.cpMin = 0;
1303 cr.cpMax = 4;
1304 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1305 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1307 /*Swap back to rich text*/
1309 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1310 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1312 /*Set the default formatting to bold italics*/
1314 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1315 cf2.dwMask |= CFM_ITALIC;
1316 cf2.dwEffects ^= CFE_ITALIC;
1317 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1318 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1320 /*Set the text in the control to "wine", which will be bold and italicized*/
1322 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1324 /*Paste the plain text "wine" string, which should take the insert
1325 formatting, which at the moment is bold italics*/
1327 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1329 /*Select the first "wine" string and retrieve its formatting*/
1331 cr.cpMin = 1;
1332 cr.cpMax = 3;
1333 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1334 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1336 /*Select the second "wine" string and retrieve its formatting*/
1338 cr.cpMin = 5;
1339 cr.cpMax = 7;
1340 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1341 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1343 /*Compare the two formattings. They should be the same.*/
1345 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1346 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1347 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1348 DestroyWindow(hwndRichEdit);
1351 static void test_WM_GETTEXT(void)
1353 HWND hwndRichEdit = new_richedit(NULL);
1354 static const char text[] = "Hello. My name is RichEdit!";
1355 static const char text2[] = "Hello. My name is RichEdit!\r";
1356 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1357 char buffer[1024] = {0};
1358 int result;
1360 /* Baseline test with normal-sized buffer */
1361 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1362 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1363 ok(result == lstrlen(buffer),
1364 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1365 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1366 result = strcmp(buffer,text);
1367 ok(result == 0,
1368 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1370 /* Test for returned value of WM_GETTEXTLENGTH */
1371 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1372 ok(result == lstrlen(text),
1373 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1374 result, lstrlen(text));
1376 /* Test for behavior in overflow case */
1377 memset(buffer, 0, 1024);
1378 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1379 ok(result == 0 ||
1380 result == lstrlenA(text) - 1, /* XP, win2k3 */
1381 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1382 result = strcmp(buffer,text);
1383 if (result)
1384 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1385 ok(result == 0,
1386 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1388 /* Baseline test with normal-sized buffer and carriage return */
1389 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1390 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1391 ok(result == lstrlen(buffer),
1392 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1393 result = strcmp(buffer,text2_after);
1394 ok(result == 0,
1395 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1397 /* Test for returned value of WM_GETTEXTLENGTH */
1398 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1399 ok(result == lstrlen(text2_after),
1400 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1401 result, lstrlen(text2_after));
1403 /* Test for behavior of CRLF conversion in case of overflow */
1404 memset(buffer, 0, 1024);
1405 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1406 ok(result == 0 ||
1407 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1408 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1409 result = strcmp(buffer,text2);
1410 if (result)
1411 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1412 ok(result == 0,
1413 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1415 DestroyWindow(hwndRichEdit);
1418 static void test_EM_GETTEXTRANGE(void)
1420 HWND hwndRichEdit = new_richedit(NULL);
1421 const char * text1 = "foo bar\r\nfoo bar";
1422 const char * text2 = "foo bar\rfoo bar";
1423 const char * expect = "bar\rfoo";
1424 char buffer[1024] = {0};
1425 LRESULT result;
1426 TEXTRANGEA textRange;
1428 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1430 textRange.lpstrText = buffer;
1431 textRange.chrg.cpMin = 4;
1432 textRange.chrg.cpMax = 11;
1433 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1434 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1435 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1437 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1439 textRange.lpstrText = buffer;
1440 textRange.chrg.cpMin = 4;
1441 textRange.chrg.cpMax = 11;
1442 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1443 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1444 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1446 /* cpMax of text length is used instead of -1 in this case */
1447 textRange.lpstrText = buffer;
1448 textRange.chrg.cpMin = 0;
1449 textRange.chrg.cpMax = -1;
1450 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1451 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1452 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1454 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1455 textRange.lpstrText = buffer;
1456 textRange.chrg.cpMin = -1;
1457 textRange.chrg.cpMax = 1;
1458 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1459 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1460 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1462 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1463 textRange.lpstrText = buffer;
1464 textRange.chrg.cpMin = 1;
1465 textRange.chrg.cpMax = -1;
1466 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1467 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1468 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1470 /* no end character is copied if cpMax - cpMin < 0 */
1471 textRange.lpstrText = buffer;
1472 textRange.chrg.cpMin = 5;
1473 textRange.chrg.cpMax = 5;
1474 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1475 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1476 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1478 /* cpMax of text length is used if cpMax > text length*/
1479 textRange.lpstrText = buffer;
1480 textRange.chrg.cpMin = 0;
1481 textRange.chrg.cpMax = 1000;
1482 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1483 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1484 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1486 DestroyWindow(hwndRichEdit);
1489 static void test_EM_GETSELTEXT(void)
1491 HWND hwndRichEdit = new_richedit(NULL);
1492 const char * text1 = "foo bar\r\nfoo bar";
1493 const char * text2 = "foo bar\rfoo bar";
1494 const char * expect = "bar\rfoo";
1495 char buffer[1024] = {0};
1496 LRESULT result;
1498 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1500 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1501 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1502 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1503 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1505 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1507 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1508 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1509 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1510 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1512 DestroyWindow(hwndRichEdit);
1515 /* FIXME: need to test unimplemented options and robustly test wparam */
1516 static void test_EM_SETOPTIONS(void)
1518 HWND hwndRichEdit;
1519 static const char text[] = "Hello. My name is RichEdit!";
1520 char buffer[1024] = {0};
1521 DWORD dwStyle, options, oldOptions;
1522 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1523 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1524 ES_SELECTIONBAR|ES_VERTICAL;
1526 /* Test initial options. */
1527 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1528 0, 0, 200, 60, NULL, NULL,
1529 hmoduleRichEdit, NULL);
1530 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1531 RICHEDIT_CLASS, (int) GetLastError());
1532 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1533 ok(options == 0, "Incorrect initial options %x\n", options);
1534 DestroyWindow(hwndRichEdit);
1536 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1537 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1538 0, 0, 200, 60, NULL, NULL,
1539 hmoduleRichEdit, NULL);
1540 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1541 RICHEDIT_CLASS, (int) GetLastError());
1542 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1543 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1544 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1545 "Incorrect initial options %x\n", options);
1547 /* NEGATIVE TESTING - NO OPTIONS SET */
1548 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1549 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1551 /* testing no readonly by sending 'a' to the control*/
1552 SetFocus(hwndRichEdit);
1553 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1554 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1555 ok(buffer[0]=='a',
1556 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1557 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1559 /* READONLY - sending 'a' to the control */
1560 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1561 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1562 SetFocus(hwndRichEdit);
1563 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1564 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1565 ok(buffer[0]==text[0],
1566 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1568 /* EM_SETOPTIONS changes the window style, but changing the
1569 * window style does not change the options. */
1570 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1571 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1572 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1573 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1574 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1575 /* Confirm that the text is still read only. */
1576 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1577 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1578 ok(buffer[0]==text[0],
1579 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1581 oldOptions = options;
1582 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1583 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1584 ok(options == oldOptions,
1585 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1587 DestroyWindow(hwndRichEdit);
1590 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1592 CHARFORMAT2W text_format;
1593 text_format.cbSize = sizeof(text_format);
1594 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1595 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1596 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1599 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1601 int link_present = 0;
1603 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1604 if (is_url)
1605 { /* control text is url; should get CFE_LINK */
1606 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1608 else
1610 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1614 static HWND new_static_wnd(HWND parent) {
1615 return new_window("Static", 0, parent);
1618 static void test_EM_AUTOURLDETECT(void)
1620 /* DO NOT change the properties of the first two elements. To shorten the
1621 tests, all tests after WM_SETTEXT test just the first two elements -
1622 one non-URL and one URL */
1623 struct urls_s {
1624 const char *text;
1625 int is_url;
1626 } urls[12] = {
1627 {"winehq.org", 0},
1628 {"http://www.winehq.org", 1},
1629 {"http//winehq.org", 0},
1630 {"ww.winehq.org", 0},
1631 {"www.winehq.org", 1},
1632 {"ftp://192.168.1.1", 1},
1633 {"ftp//192.168.1.1", 0},
1634 {"mailto:your@email.com", 1},
1635 {"prospero:prosperoserver", 1},
1636 {"telnet:test", 1},
1637 {"news:newserver", 1},
1638 {"wais:waisserver", 1}
1641 int i, j;
1642 int urlRet=-1;
1643 HWND hwndRichEdit, parent;
1645 /* All of the following should cause the URL to be detected */
1646 const char * templates_delim[] = {
1647 "This is some text with X on it",
1648 "This is some text with (X) on it",
1649 "This is some text with X\r on it",
1650 "This is some text with ---X--- on it",
1651 "This is some text with \"X\" on it",
1652 "This is some text with 'X' on it",
1653 "This is some text with 'X' on it",
1654 "This is some text with :X: on it",
1656 "This text ends with X",
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",
1664 "This is some text with (X on it",
1665 "This is some text with \rX on it",
1666 "This is some text with ---X on it",
1667 "This is some text with \"X on it",
1668 "This is some text with 'X on it",
1669 "This is some text with :X on it",
1671 /* None of these should cause the URL to be detected */
1672 const char * templates_non_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",
1681 "This is some text with |X on it",
1682 "This is some text with *X on it",
1683 "This is some text with /X on it",
1684 "This is some text with +X on it",
1685 "This is some text with %X on it",
1686 "This is some text with #X on it",
1687 "This is some text with @X on it",
1688 "This is some text with \\X on it",
1690 /* All of these cause the URL detection to be extended by one more byte,
1691 thus demonstrating that the tested character is considered as part
1692 of the URL. */
1693 const char * templates_xten_delim[] = {
1694 "This is some text with X| on it",
1695 "This is some text with X* on it",
1696 "This is some text with X/ on it",
1697 "This is some text with X+ on it",
1698 "This is some text with X% on it",
1699 "This is some text with X# on it",
1700 "This is some text with X@ on it",
1701 "This is some text with X\\ on it",
1703 char buffer[1024];
1705 parent = new_static_wnd(NULL);
1706 hwndRichEdit = new_richedit(parent);
1707 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1708 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1709 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1710 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1711 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1712 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1713 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1714 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1715 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1716 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1717 /* for each url, check the text to see if CFE_LINK effect is present */
1718 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1720 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1721 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1722 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1724 /* Link detection should happen immediately upon WM_SETTEXT */
1725 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1726 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1727 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1729 DestroyWindow(hwndRichEdit);
1731 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1732 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1733 hwndRichEdit = new_richedit(parent);
1735 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1736 char * at_pos;
1737 int at_offset;
1738 int end_offset;
1740 at_pos = strchr(templates_delim[j], 'X');
1741 at_offset = at_pos - templates_delim[j];
1742 strncpy(buffer, templates_delim[j], at_offset);
1743 buffer[at_offset] = '\0';
1744 strcat(buffer, urls[i].text);
1745 strcat(buffer, templates_delim[j] + at_offset + 1);
1746 end_offset = at_offset + strlen(urls[i].text);
1748 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1749 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1751 /* This assumes no templates start with the URL itself, and that they
1752 have at least two characters before the URL text */
1753 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1754 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1755 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1756 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1757 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1758 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1760 if (urls[i].is_url)
1762 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1763 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1764 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1765 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1767 else
1769 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1770 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1771 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1772 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1774 if (buffer[end_offset] != '\0')
1776 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1777 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1778 if (buffer[end_offset +1] != '\0')
1780 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1781 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1786 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1787 char * at_pos;
1788 int at_offset;
1789 int end_offset;
1791 at_pos = strchr(templates_non_delim[j], 'X');
1792 at_offset = at_pos - templates_non_delim[j];
1793 strncpy(buffer, templates_non_delim[j], at_offset);
1794 buffer[at_offset] = '\0';
1795 strcat(buffer, urls[i].text);
1796 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1797 end_offset = at_offset + strlen(urls[i].text);
1799 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1800 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1802 /* This assumes no templates start with the URL itself, and that they
1803 have at least two characters before the URL text */
1804 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1805 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1806 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1807 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1808 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1809 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1811 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1812 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1813 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1814 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1815 if (buffer[end_offset] != '\0')
1817 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1818 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1819 if (buffer[end_offset +1] != '\0')
1821 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1822 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1827 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1828 char * at_pos;
1829 int at_offset;
1830 int end_offset;
1832 at_pos = strchr(templates_xten_delim[j], 'X');
1833 at_offset = at_pos - templates_xten_delim[j];
1834 strncpy(buffer, templates_xten_delim[j], at_offset);
1835 buffer[at_offset] = '\0';
1836 strcat(buffer, urls[i].text);
1837 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1838 end_offset = at_offset + strlen(urls[i].text);
1840 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1841 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1843 /* This assumes no templates start with the URL itself, and that they
1844 have at least two characters before the URL text */
1845 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1846 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1847 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1848 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1849 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1850 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1852 if (urls[i].is_url)
1854 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1855 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1856 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1857 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1858 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1859 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1861 else
1863 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1864 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1865 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1866 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1867 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1868 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1870 if (buffer[end_offset +1] != '\0')
1872 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1873 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1874 if (buffer[end_offset +2] != '\0')
1876 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1877 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1882 DestroyWindow(hwndRichEdit);
1883 hwndRichEdit = NULL;
1886 /* Test detection of URLs within normal text - WM_CHAR case. */
1887 /* Test only the first two URL examples for brevity */
1888 for (i = 0; i < 2; i++) {
1889 hwndRichEdit = new_richedit(parent);
1891 /* Also for brevity, test only the first three delimiters */
1892 for (j = 0; j < 3; j++) {
1893 char * at_pos;
1894 int at_offset;
1895 int end_offset;
1896 int u, v;
1898 at_pos = strchr(templates_delim[j], 'X');
1899 at_offset = at_pos - templates_delim[j];
1900 end_offset = at_offset + strlen(urls[i].text);
1902 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1903 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1904 for (u = 0; templates_delim[j][u]; u++) {
1905 if (templates_delim[j][u] == '\r') {
1906 simulate_typing_characters(hwndRichEdit, "\r");
1907 } else if (templates_delim[j][u] != 'X') {
1908 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1909 } else {
1910 for (v = 0; urls[i].text[v]; v++) {
1911 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1915 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1917 /* This assumes no templates start with the URL itself, and that they
1918 have at least two characters before the URL text */
1919 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1920 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1921 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1922 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1923 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1924 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1926 if (urls[i].is_url)
1928 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1929 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1930 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1931 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1933 else
1935 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1936 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1937 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1938 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1940 if (buffer[end_offset] != '\0')
1942 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1943 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1944 if (buffer[end_offset +1] != '\0')
1946 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1947 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1951 /* The following will insert a paragraph break after the first character
1952 of the URL candidate, thus breaking the URL. It is expected that the
1953 CFE_LINK attribute should break across both pieces of the URL */
1954 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1955 simulate_typing_characters(hwndRichEdit, "\r");
1956 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1958 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1959 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1960 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1961 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1962 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1963 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1965 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1966 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1967 /* end_offset moved because of paragraph break */
1968 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1969 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1970 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1971 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1973 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1974 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1975 if (buffer[end_offset +2] != '\0')
1977 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1978 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1982 /* The following will remove the just-inserted paragraph break, thus
1983 restoring the URL */
1984 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1985 simulate_typing_characters(hwndRichEdit, "\b");
1986 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1988 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1989 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1990 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1991 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1992 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1993 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1995 if (urls[i].is_url)
1997 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1998 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1999 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2000 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2002 else
2004 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2005 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2006 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2007 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2009 if (buffer[end_offset] != '\0')
2011 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2012 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2013 if (buffer[end_offset +1] != '\0')
2015 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2016 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2020 DestroyWindow(hwndRichEdit);
2021 hwndRichEdit = NULL;
2024 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2025 /* Test just the first two URL examples for brevity */
2026 for (i = 0; i < 2; i++) {
2027 SETTEXTEX st;
2029 hwndRichEdit = new_richedit(parent);
2031 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2032 be detected:
2033 1) Set entire text, a la WM_SETTEXT
2034 2) Set a selection of the text to the URL
2035 3) Set a portion of the text at a time, which eventually results in
2036 an URL
2037 All of them should give equivalent results
2040 /* Set entire text in one go, like WM_SETTEXT */
2041 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2042 char * at_pos;
2043 int at_offset;
2044 int end_offset;
2046 st.codepage = CP_ACP;
2047 st.flags = ST_DEFAULT;
2049 at_pos = strchr(templates_delim[j], 'X');
2050 at_offset = at_pos - templates_delim[j];
2051 strncpy(buffer, templates_delim[j], at_offset);
2052 buffer[at_offset] = '\0';
2053 strcat(buffer, urls[i].text);
2054 strcat(buffer, templates_delim[j] + at_offset + 1);
2055 end_offset = at_offset + strlen(urls[i].text);
2057 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2058 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2060 /* This assumes no templates start with the URL itself, and that they
2061 have at least two characters before the URL text */
2062 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2063 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2064 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2065 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2066 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2067 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2069 if (urls[i].is_url)
2071 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2072 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2073 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2074 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2076 else
2078 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2079 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2080 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2081 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2083 if (buffer[end_offset] != '\0')
2085 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2086 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2087 if (buffer[end_offset +1] != '\0')
2089 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2090 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2095 /* Set selection with X to the URL */
2096 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2097 char * at_pos;
2098 int at_offset;
2099 int end_offset;
2101 at_pos = strchr(templates_delim[j], 'X');
2102 at_offset = at_pos - templates_delim[j];
2103 end_offset = at_offset + strlen(urls[i].text);
2105 st.codepage = CP_ACP;
2106 st.flags = ST_DEFAULT;
2107 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2108 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2109 st.flags = ST_SELECTION;
2110 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2111 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2112 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2114 /* This assumes no templates start with the URL itself, and that they
2115 have at least two characters before the URL text */
2116 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2117 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2118 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2119 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2120 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2121 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2123 if (urls[i].is_url)
2125 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2126 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2127 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2128 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2130 else
2132 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2133 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2134 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2135 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2137 if (buffer[end_offset] != '\0')
2139 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2140 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2141 if (buffer[end_offset +1] != '\0')
2143 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2144 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2149 /* Set selection with X to the first character of the URL, then the rest */
2150 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2151 char * at_pos;
2152 int at_offset;
2153 int end_offset;
2155 at_pos = strchr(templates_delim[j], 'X');
2156 at_offset = at_pos - templates_delim[j];
2157 end_offset = at_offset + strlen(urls[i].text);
2159 strcpy(buffer, "YY");
2160 buffer[0] = urls[i].text[0];
2162 st.codepage = CP_ACP;
2163 st.flags = ST_DEFAULT;
2164 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2165 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2166 st.flags = ST_SELECTION;
2167 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2168 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2169 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2170 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2171 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2173 /* This assumes no templates start with the URL itself, and that they
2174 have at least two characters before the URL text */
2175 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2176 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2177 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2178 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2179 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2180 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2182 if (urls[i].is_url)
2184 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2185 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2186 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2187 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2189 else
2191 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2192 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2193 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2194 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2196 if (buffer[end_offset] != '\0')
2198 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2199 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2200 if (buffer[end_offset +1] != '\0')
2202 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2203 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2208 DestroyWindow(hwndRichEdit);
2209 hwndRichEdit = NULL;
2212 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2213 /* Test just the first two URL examples for brevity */
2214 for (i = 0; i < 2; i++) {
2215 hwndRichEdit = new_richedit(parent);
2217 /* Set selection with X to the URL */
2218 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2219 char * at_pos;
2220 int at_offset;
2221 int end_offset;
2223 at_pos = strchr(templates_delim[j], 'X');
2224 at_offset = at_pos - templates_delim[j];
2225 end_offset = at_offset + strlen(urls[i].text);
2227 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2228 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2229 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2230 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2231 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2233 /* This assumes no templates start with the URL itself, and that they
2234 have at least two characters before the URL text */
2235 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2236 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2237 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2238 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2239 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2240 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2242 if (urls[i].is_url)
2244 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2245 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2246 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2247 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2249 else
2251 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2252 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2253 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2254 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2256 if (buffer[end_offset] != '\0')
2258 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2259 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2260 if (buffer[end_offset +1] != '\0')
2262 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2263 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2268 /* Set selection with X to the first character of the URL, then the rest */
2269 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2270 char * at_pos;
2271 int at_offset;
2272 int end_offset;
2274 at_pos = strchr(templates_delim[j], 'X');
2275 at_offset = at_pos - templates_delim[j];
2276 end_offset = at_offset + strlen(urls[i].text);
2278 strcpy(buffer, "YY");
2279 buffer[0] = urls[i].text[0];
2281 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2282 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2283 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2284 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2285 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2286 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2287 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2289 /* This assumes no templates start with the URL itself, and that they
2290 have at least two characters before the URL text */
2291 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2292 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2293 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2294 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2295 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2296 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2298 if (urls[i].is_url)
2300 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2301 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2302 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2303 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2305 else
2307 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2308 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2309 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2310 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2312 if (buffer[end_offset] != '\0')
2314 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2315 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2316 if (buffer[end_offset +1] != '\0')
2318 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2319 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2324 DestroyWindow(hwndRichEdit);
2325 hwndRichEdit = NULL;
2328 DestroyWindow(parent);
2331 static void test_EM_SCROLL(void)
2333 int i, j;
2334 int r; /* return value */
2335 int expr; /* expected return value */
2336 HWND hwndRichEdit = new_richedit(NULL);
2337 int y_before, y_after; /* units of lines of text */
2339 /* test a richedit box containing a single line of text */
2340 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2341 expr = 0x00010000;
2342 for (i = 0; i < 4; i++) {
2343 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2345 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2346 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2347 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2348 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2349 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2350 "(i == %d)\n", y_after, i);
2354 * test a richedit box that will scroll. There are two general
2355 * cases: the case without any long lines and the case with a long
2356 * line.
2358 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2359 if (i == 0)
2360 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2361 else
2362 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2363 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2364 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2365 "LONG LINE \nb\nc\nd\ne");
2366 for (j = 0; j < 12; j++) /* reset scroll position to top */
2367 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2369 /* get first visible line */
2370 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2371 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2373 /* get new current first visible line */
2374 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2376 ok(((r & 0xffffff00) == 0x00010000) &&
2377 ((r & 0x000000ff) != 0x00000000),
2378 "EM_SCROLL page down didn't scroll by a small positive number of "
2379 "lines (r == 0x%08x)\n", r);
2380 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2381 "(line %d scrolled to line %d\n", y_before, y_after);
2383 y_before = y_after;
2385 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2386 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2387 ok(((r & 0xffffff00) == 0x0001ff00),
2388 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2389 "(r == 0x%08x)\n", r);
2390 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2391 "%d scrolled to line %d\n", y_before, y_after);
2393 y_before = y_after;
2395 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2397 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2399 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2400 "(r == 0x%08x)\n", r);
2401 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2402 "1 line (%d scrolled to %d)\n", y_before, y_after);
2404 y_before = y_after;
2406 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2408 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2410 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2411 "(r == 0x%08x)\n", r);
2412 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2413 "line (%d scrolled to %d)\n", y_before, y_after);
2415 y_before = y_after;
2417 r = SendMessage(hwndRichEdit, EM_SCROLL,
2418 SB_LINEUP, 0); /* lineup beyond top */
2420 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2422 ok(r == 0x00010000,
2423 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2424 ok(y_before == y_after,
2425 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2427 y_before = y_after;
2429 r = SendMessage(hwndRichEdit, EM_SCROLL,
2430 SB_PAGEUP, 0);/*page up beyond top */
2432 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2434 ok(r == 0x00010000,
2435 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2436 ok(y_before == y_after,
2437 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2439 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2440 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2441 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2442 r = SendMessage(hwndRichEdit, EM_SCROLL,
2443 SB_PAGEDOWN, 0); /* page down beyond bot */
2444 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2446 ok(r == 0x00010000,
2447 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2448 ok(y_before == y_after,
2449 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2450 y_before, y_after);
2452 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2453 SendMessage(hwndRichEdit, EM_SCROLL,
2454 SB_LINEDOWN, 0); /* line down beyond bot */
2455 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2457 ok(r == 0x00010000,
2458 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2459 ok(y_before == y_after,
2460 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2461 y_before, y_after);
2463 DestroyWindow(hwndRichEdit);
2466 static unsigned int recursionLevel = 0;
2467 static unsigned int WM_SIZE_recursionLevel = 0;
2468 static BOOL bailedOutOfRecursion = FALSE;
2469 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2471 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2473 LRESULT r;
2475 if (bailedOutOfRecursion) return 0;
2476 if (recursionLevel >= 32) {
2477 bailedOutOfRecursion = TRUE;
2478 return 0;
2481 recursionLevel++;
2482 switch (message) {
2483 case WM_SIZE:
2484 WM_SIZE_recursionLevel++;
2485 r = richeditProc(hwnd, message, wParam, lParam);
2486 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2487 ShowScrollBar(hwnd, SB_VERT, TRUE);
2488 WM_SIZE_recursionLevel--;
2489 break;
2490 default:
2491 r = richeditProc(hwnd, message, wParam, lParam);
2492 break;
2494 recursionLevel--;
2495 return r;
2498 static void test_scrollbar_visibility(void)
2500 HWND hwndRichEdit;
2501 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2502 SCROLLINFO si;
2503 WNDCLASSA cls;
2504 BOOL r;
2506 /* These tests show that richedit should temporarily refrain from automatically
2507 hiding or showing its scrollbars (vertical at least) when an explicit request
2508 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2509 Some applications depend on forced showing (when otherwise richedit would
2510 hide the vertical scrollbar) and are thrown on an endless recursive loop
2511 if richedit auto-hides the scrollbar again. Apparently they never heard of
2512 the ES_DISABLENOSCROLL style... */
2514 hwndRichEdit = new_richedit(NULL);
2516 /* Test default scrollbar visibility behavior */
2517 memset(&si, 0, sizeof(si));
2518 si.cbSize = sizeof(si);
2519 si.fMask = SIF_PAGE | SIF_RANGE;
2520 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2521 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2522 "Vertical scrollbar is visible, should be invisible.\n");
2523 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2524 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2525 si.nPage, si.nMin, si.nMax);
2527 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2528 memset(&si, 0, sizeof(si));
2529 si.cbSize = sizeof(si);
2530 si.fMask = SIF_PAGE | SIF_RANGE;
2531 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2532 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2533 "Vertical scrollbar is visible, should be invisible.\n");
2534 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2535 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2536 si.nPage, si.nMin, si.nMax);
2538 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2539 memset(&si, 0, sizeof(si));
2540 si.cbSize = sizeof(si);
2541 si.fMask = SIF_PAGE | SIF_RANGE;
2542 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2543 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2544 "Vertical scrollbar is invisible, should be visible.\n");
2545 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2546 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2547 si.nPage, si.nMin, si.nMax);
2549 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2550 even though it hides the scrollbar */
2551 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2552 memset(&si, 0, sizeof(si));
2553 si.cbSize = sizeof(si);
2554 si.fMask = SIF_PAGE | SIF_RANGE;
2555 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2556 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2557 "Vertical scrollbar is visible, should be invisible.\n");
2558 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2559 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2560 si.nPage, si.nMin, si.nMax);
2562 /* Setting non-scrolling text again does *not* reset scrollbar range */
2563 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2564 memset(&si, 0, sizeof(si));
2565 si.cbSize = sizeof(si);
2566 si.fMask = SIF_PAGE | SIF_RANGE;
2567 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2568 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2569 "Vertical scrollbar is visible, should be invisible.\n");
2570 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2571 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2572 si.nPage, si.nMin, si.nMax);
2574 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2575 memset(&si, 0, sizeof(si));
2576 si.cbSize = sizeof(si);
2577 si.fMask = SIF_PAGE | SIF_RANGE;
2578 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2579 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2580 "Vertical scrollbar is visible, should be invisible.\n");
2581 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2582 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2583 si.nPage, si.nMin, si.nMax);
2585 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2586 memset(&si, 0, sizeof(si));
2587 si.cbSize = sizeof(si);
2588 si.fMask = SIF_PAGE | SIF_RANGE;
2589 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2590 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2591 "Vertical scrollbar is visible, should be invisible.\n");
2592 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2593 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2594 si.nPage, si.nMin, si.nMax);
2596 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2597 memset(&si, 0, sizeof(si));
2598 si.cbSize = sizeof(si);
2599 si.fMask = SIF_PAGE | SIF_RANGE;
2600 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2601 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2602 "Vertical scrollbar is visible, should be invisible.\n");
2603 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2604 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2605 si.nPage, si.nMin, si.nMax);
2607 DestroyWindow(hwndRichEdit);
2609 /* Test again, with ES_DISABLENOSCROLL style */
2610 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2612 /* Test default scrollbar visibility behavior */
2613 memset(&si, 0, sizeof(si));
2614 si.cbSize = sizeof(si);
2615 si.fMask = SIF_PAGE | SIF_RANGE;
2616 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2617 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2618 "Vertical scrollbar is invisible, should be visible.\n");
2619 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2620 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2621 si.nPage, si.nMin, si.nMax);
2623 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2624 memset(&si, 0, sizeof(si));
2625 si.cbSize = sizeof(si);
2626 si.fMask = SIF_PAGE | SIF_RANGE;
2627 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2628 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2629 "Vertical scrollbar is invisible, should be visible.\n");
2630 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2631 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2632 si.nPage, si.nMin, si.nMax);
2634 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2635 memset(&si, 0, sizeof(si));
2636 si.cbSize = sizeof(si);
2637 si.fMask = SIF_PAGE | SIF_RANGE;
2638 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2639 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2640 "Vertical scrollbar is invisible, should be visible.\n");
2641 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2642 "reported page/range is %d (%d..%d)\n",
2643 si.nPage, si.nMin, si.nMax);
2645 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2646 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2647 memset(&si, 0, sizeof(si));
2648 si.cbSize = sizeof(si);
2649 si.fMask = SIF_PAGE | SIF_RANGE;
2650 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2651 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2652 "Vertical scrollbar is invisible, should be visible.\n");
2653 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2654 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2655 si.nPage, si.nMin, si.nMax);
2657 /* Setting non-scrolling text again does *not* reset scrollbar range */
2658 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2659 memset(&si, 0, sizeof(si));
2660 si.cbSize = sizeof(si);
2661 si.fMask = SIF_PAGE | SIF_RANGE;
2662 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2663 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2664 "Vertical scrollbar is invisible, should be visible.\n");
2665 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2666 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2667 si.nPage, si.nMin, si.nMax);
2669 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2670 memset(&si, 0, sizeof(si));
2671 si.cbSize = sizeof(si);
2672 si.fMask = SIF_PAGE | SIF_RANGE;
2673 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2674 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2675 "Vertical scrollbar is invisible, should be visible.\n");
2676 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2677 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2678 si.nPage, si.nMin, si.nMax);
2680 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2681 memset(&si, 0, sizeof(si));
2682 si.cbSize = sizeof(si);
2683 si.fMask = SIF_PAGE | SIF_RANGE;
2684 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2685 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2686 "Vertical scrollbar is invisible, should be visible.\n");
2687 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2688 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2689 si.nPage, si.nMin, si.nMax);
2691 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2692 memset(&si, 0, sizeof(si));
2693 si.cbSize = sizeof(si);
2694 si.fMask = SIF_PAGE | SIF_RANGE;
2695 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2696 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2697 "Vertical scrollbar is invisible, should be visible.\n");
2698 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2699 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2700 si.nPage, si.nMin, si.nMax);
2702 DestroyWindow(hwndRichEdit);
2704 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2705 hwndRichEdit = new_richedit(NULL);
2707 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2708 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2709 memset(&si, 0, sizeof(si));
2710 si.cbSize = sizeof(si);
2711 si.fMask = SIF_PAGE | SIF_RANGE;
2712 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2713 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2714 "Vertical scrollbar is invisible, should be visible.\n");
2715 todo_wine {
2716 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2717 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2718 si.nPage, si.nMin, si.nMax);
2721 /* Ditto, see above */
2722 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2723 memset(&si, 0, sizeof(si));
2724 si.cbSize = sizeof(si);
2725 si.fMask = SIF_PAGE | SIF_RANGE;
2726 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2727 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2728 "Vertical scrollbar is invisible, should be visible.\n");
2729 todo_wine {
2730 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2731 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2732 si.nPage, si.nMin, si.nMax);
2735 /* Ditto, see above */
2736 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2737 memset(&si, 0, sizeof(si));
2738 si.cbSize = sizeof(si);
2739 si.fMask = SIF_PAGE | SIF_RANGE;
2740 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2741 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2742 "Vertical scrollbar is invisible, should be visible.\n");
2743 todo_wine {
2744 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2745 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2746 si.nPage, si.nMin, si.nMax);
2749 /* Ditto, see above */
2750 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2751 memset(&si, 0, sizeof(si));
2752 si.cbSize = sizeof(si);
2753 si.fMask = SIF_PAGE | SIF_RANGE;
2754 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2755 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2756 "Vertical scrollbar is invisible, should be visible.\n");
2757 todo_wine {
2758 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2759 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2760 si.nPage, si.nMin, si.nMax);
2763 /* Ditto, see above */
2764 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2765 memset(&si, 0, sizeof(si));
2766 si.cbSize = sizeof(si);
2767 si.fMask = SIF_PAGE | SIF_RANGE;
2768 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2769 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2770 "Vertical scrollbar is invisible, should be visible.\n");
2771 todo_wine {
2772 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2773 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2774 si.nPage, si.nMin, si.nMax);
2777 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2778 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2779 memset(&si, 0, sizeof(si));
2780 si.cbSize = sizeof(si);
2781 si.fMask = SIF_PAGE | SIF_RANGE;
2782 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2783 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2784 "Vertical scrollbar is visible, should be invisible.\n");
2785 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2786 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2787 si.nPage, si.nMin, si.nMax);
2789 DestroyWindow(hwndRichEdit);
2791 hwndRichEdit = new_richedit(NULL);
2793 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2794 memset(&si, 0, sizeof(si));
2795 si.cbSize = sizeof(si);
2796 si.fMask = SIF_PAGE | SIF_RANGE;
2797 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2798 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2799 "Vertical scrollbar is visible, should be invisible.\n");
2800 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2801 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2802 si.nPage, si.nMin, si.nMax);
2804 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2805 memset(&si, 0, sizeof(si));
2806 si.cbSize = sizeof(si);
2807 si.fMask = SIF_PAGE | SIF_RANGE;
2808 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2809 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2810 "Vertical scrollbar is visible, should be invisible.\n");
2811 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2812 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2813 si.nPage, si.nMin, si.nMax);
2815 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2816 memset(&si, 0, sizeof(si));
2817 si.cbSize = sizeof(si);
2818 si.fMask = SIF_PAGE | SIF_RANGE;
2819 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2820 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2821 "Vertical scrollbar is visible, should be invisible.\n");
2822 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2823 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2824 si.nPage, si.nMin, si.nMax);
2826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2827 memset(&si, 0, sizeof(si));
2828 si.cbSize = sizeof(si);
2829 si.fMask = SIF_PAGE | SIF_RANGE;
2830 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2832 "Vertical scrollbar is visible, should be invisible.\n");
2833 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2834 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2835 si.nPage, si.nMin, si.nMax);
2837 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2838 memset(&si, 0, sizeof(si));
2839 si.cbSize = sizeof(si);
2840 si.fMask = SIF_PAGE | SIF_RANGE;
2841 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2842 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2843 "Vertical scrollbar is invisible, should be visible.\n");
2844 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2845 "reported page/range is %d (%d..%d)\n",
2846 si.nPage, si.nMin, si.nMax);
2848 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2849 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2850 memset(&si, 0, sizeof(si));
2851 si.cbSize = sizeof(si);
2852 si.fMask = SIF_PAGE | SIF_RANGE;
2853 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2854 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2855 "Vertical scrollbar is visible, should be invisible.\n");
2856 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2857 "reported page/range is %d (%d..%d)\n",
2858 si.nPage, si.nMin, si.nMax);
2860 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2861 memset(&si, 0, sizeof(si));
2862 si.cbSize = sizeof(si);
2863 si.fMask = SIF_PAGE | SIF_RANGE;
2864 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2865 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2866 "Vertical scrollbar is visible, should be invisible.\n");
2867 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2868 "reported page/range is %d (%d..%d)\n",
2869 si.nPage, si.nMin, si.nMax);
2871 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2872 EM_SCROLL will make visible any forcefully invisible scrollbar */
2873 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2874 memset(&si, 0, sizeof(si));
2875 si.cbSize = sizeof(si);
2876 si.fMask = SIF_PAGE | SIF_RANGE;
2877 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2878 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2879 "Vertical scrollbar is invisible, should be visible.\n");
2880 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2881 "reported page/range is %d (%d..%d)\n",
2882 si.nPage, si.nMin, si.nMax);
2884 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2885 memset(&si, 0, sizeof(si));
2886 si.cbSize = sizeof(si);
2887 si.fMask = SIF_PAGE | SIF_RANGE;
2888 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2889 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2890 "Vertical scrollbar is visible, should be invisible.\n");
2891 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2892 "reported page/range is %d (%d..%d)\n",
2893 si.nPage, si.nMin, si.nMax);
2895 /* Again, EM_SCROLL, with SB_LINEUP */
2896 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2897 memset(&si, 0, sizeof(si));
2898 si.cbSize = sizeof(si);
2899 si.fMask = SIF_PAGE | SIF_RANGE;
2900 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2901 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2902 "Vertical scrollbar is invisible, should be visible.\n");
2903 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2904 "reported page/range is %d (%d..%d)\n",
2905 si.nPage, si.nMin, si.nMax);
2907 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2908 memset(&si, 0, sizeof(si));
2909 si.cbSize = sizeof(si);
2910 si.fMask = SIF_PAGE | SIF_RANGE;
2911 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2912 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2913 "Vertical scrollbar is visible, should be invisible.\n");
2914 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2915 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2916 si.nPage, si.nMin, si.nMax);
2918 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2919 memset(&si, 0, sizeof(si));
2920 si.cbSize = sizeof(si);
2921 si.fMask = SIF_PAGE | SIF_RANGE;
2922 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2923 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2924 "Vertical scrollbar is invisible, should be visible.\n");
2925 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2926 "reported page/range is %d (%d..%d)\n",
2927 si.nPage, si.nMin, si.nMax);
2929 DestroyWindow(hwndRichEdit);
2932 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2933 hwndRichEdit = new_richedit(NULL);
2935 #define ENABLE_WS_VSCROLL(hwnd) \
2936 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2937 #define DISABLE_WS_VSCROLL(hwnd) \
2938 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2940 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2941 ENABLE_WS_VSCROLL(hwndRichEdit);
2942 memset(&si, 0, sizeof(si));
2943 si.cbSize = sizeof(si);
2944 si.fMask = SIF_PAGE | SIF_RANGE;
2945 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2946 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2947 "Vertical scrollbar is invisible, should be visible.\n");
2948 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2949 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2950 si.nPage, si.nMin, si.nMax);
2952 /* Ditto, see above */
2953 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2954 memset(&si, 0, sizeof(si));
2955 si.cbSize = sizeof(si);
2956 si.fMask = SIF_PAGE | SIF_RANGE;
2957 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2958 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2959 "Vertical scrollbar is invisible, should be visible.\n");
2960 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2961 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2962 si.nPage, si.nMin, si.nMax);
2964 /* Ditto, see above */
2965 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2966 memset(&si, 0, sizeof(si));
2967 si.cbSize = sizeof(si);
2968 si.fMask = SIF_PAGE | SIF_RANGE;
2969 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2970 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2971 "Vertical scrollbar is invisible, should be visible.\n");
2972 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2973 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2974 si.nPage, si.nMin, si.nMax);
2976 /* Ditto, see above */
2977 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2978 memset(&si, 0, sizeof(si));
2979 si.cbSize = sizeof(si);
2980 si.fMask = SIF_PAGE | SIF_RANGE;
2981 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2982 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2983 "Vertical scrollbar is invisible, should be visible.\n");
2984 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2985 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2986 si.nPage, si.nMin, si.nMax);
2988 /* Ditto, see above */
2989 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2990 memset(&si, 0, sizeof(si));
2991 si.cbSize = sizeof(si);
2992 si.fMask = SIF_PAGE | SIF_RANGE;
2993 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2994 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2995 "Vertical scrollbar is invisible, should be visible.\n");
2996 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2997 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2998 si.nPage, si.nMin, si.nMax);
3000 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3001 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3002 memset(&si, 0, sizeof(si));
3003 si.cbSize = sizeof(si);
3004 si.fMask = SIF_PAGE | SIF_RANGE;
3005 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3006 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3007 "Vertical scrollbar is visible, should be invisible.\n");
3008 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3009 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3010 si.nPage, si.nMin, si.nMax);
3012 DestroyWindow(hwndRichEdit);
3014 hwndRichEdit = new_richedit(NULL);
3016 DISABLE_WS_VSCROLL(hwndRichEdit);
3017 memset(&si, 0, sizeof(si));
3018 si.cbSize = sizeof(si);
3019 si.fMask = SIF_PAGE | SIF_RANGE;
3020 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3021 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3022 "Vertical scrollbar is visible, should be invisible.\n");
3023 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3024 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3025 si.nPage, si.nMin, si.nMax);
3027 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3028 memset(&si, 0, sizeof(si));
3029 si.cbSize = sizeof(si);
3030 si.fMask = SIF_PAGE | SIF_RANGE;
3031 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3032 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3033 "Vertical scrollbar is visible, should be invisible.\n");
3034 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3035 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3036 si.nPage, si.nMin, si.nMax);
3038 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3039 memset(&si, 0, sizeof(si));
3040 si.cbSize = sizeof(si);
3041 si.fMask = SIF_PAGE | SIF_RANGE;
3042 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3043 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3044 "Vertical scrollbar is visible, should be invisible.\n");
3045 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3046 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3047 si.nPage, si.nMin, si.nMax);
3049 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3050 memset(&si, 0, sizeof(si));
3051 si.cbSize = sizeof(si);
3052 si.fMask = SIF_PAGE | SIF_RANGE;
3053 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3054 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3055 "Vertical scrollbar is visible, should be invisible.\n");
3056 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3057 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3058 si.nPage, si.nMin, si.nMax);
3060 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3061 memset(&si, 0, sizeof(si));
3062 si.cbSize = sizeof(si);
3063 si.fMask = SIF_PAGE | SIF_RANGE;
3064 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3065 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3066 "Vertical scrollbar is invisible, should be visible.\n");
3067 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3068 "reported page/range is %d (%d..%d)\n",
3069 si.nPage, si.nMin, si.nMax);
3071 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3072 DISABLE_WS_VSCROLL(hwndRichEdit);
3073 memset(&si, 0, sizeof(si));
3074 si.cbSize = sizeof(si);
3075 si.fMask = SIF_PAGE | SIF_RANGE;
3076 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3077 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3078 "Vertical scrollbar is visible, should be invisible.\n");
3079 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3080 "reported page/range is %d (%d..%d)\n",
3081 si.nPage, si.nMin, si.nMax);
3083 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3084 memset(&si, 0, sizeof(si));
3085 si.cbSize = sizeof(si);
3086 si.fMask = SIF_PAGE | SIF_RANGE;
3087 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3088 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3089 "Vertical scrollbar is visible, should be invisible.\n");
3090 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3091 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3092 si.nPage, si.nMin, si.nMax);
3094 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3095 memset(&si, 0, sizeof(si));
3096 si.cbSize = sizeof(si);
3097 si.fMask = SIF_PAGE | SIF_RANGE;
3098 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3099 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3100 "Vertical scrollbar is invisible, should be visible.\n");
3101 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3102 "reported page/range is %d (%d..%d)\n",
3103 si.nPage, si.nMin, si.nMax);
3105 DISABLE_WS_VSCROLL(hwndRichEdit);
3106 memset(&si, 0, sizeof(si));
3107 si.cbSize = sizeof(si);
3108 si.fMask = SIF_PAGE | SIF_RANGE;
3109 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3110 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3111 "Vertical scrollbar is visible, should be invisible.\n");
3112 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3113 "reported page/range is %d (%d..%d)\n",
3114 si.nPage, si.nMin, si.nMax);
3116 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3117 EM_SCROLL will make visible any forcefully invisible scrollbar */
3118 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3119 memset(&si, 0, sizeof(si));
3120 si.cbSize = sizeof(si);
3121 si.fMask = SIF_PAGE | SIF_RANGE;
3122 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3123 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3124 "Vertical scrollbar is invisible, should be visible.\n");
3125 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3126 "reported page/range is %d (%d..%d)\n",
3127 si.nPage, si.nMin, si.nMax);
3129 DISABLE_WS_VSCROLL(hwndRichEdit);
3130 memset(&si, 0, sizeof(si));
3131 si.cbSize = sizeof(si);
3132 si.fMask = SIF_PAGE | SIF_RANGE;
3133 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3134 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3135 "Vertical scrollbar is visible, should be invisible.\n");
3136 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3137 "reported page/range is %d (%d..%d)\n",
3138 si.nPage, si.nMin, si.nMax);
3140 /* Again, EM_SCROLL, with SB_LINEUP */
3141 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3142 memset(&si, 0, sizeof(si));
3143 si.cbSize = sizeof(si);
3144 si.fMask = SIF_PAGE | SIF_RANGE;
3145 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3146 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3147 "Vertical scrollbar is invisible, should be visible.\n");
3148 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3149 "reported page/range is %d (%d..%d)\n",
3150 si.nPage, si.nMin, si.nMax);
3152 DestroyWindow(hwndRichEdit);
3154 /* This window proc models what is going on with Corman Lisp 3.0.
3155 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3156 force the scrollbar into visibility. Recursion should NOT happen
3157 as a result of this action.
3159 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3160 if (r) {
3161 richeditProc = cls.lpfnWndProc;
3162 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3163 cls.lpszClassName = "RicheditStupidOverride";
3164 if(!RegisterClassA(&cls)) assert(0);
3166 recursionLevel = 0;
3167 WM_SIZE_recursionLevel = 0;
3168 bailedOutOfRecursion = FALSE;
3169 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3170 ok(!bailedOutOfRecursion,
3171 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3173 recursionLevel = 0;
3174 WM_SIZE_recursionLevel = 0;
3175 bailedOutOfRecursion = FALSE;
3176 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3177 ok(!bailedOutOfRecursion,
3178 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3180 /* Unblock window in order to process WM_DESTROY */
3181 recursionLevel = 0;
3182 bailedOutOfRecursion = FALSE;
3183 WM_SIZE_recursionLevel = 0;
3184 DestroyWindow(hwndRichEdit);
3188 static void test_EM_SETUNDOLIMIT(void)
3190 /* cases we test for:
3191 * default behaviour - limiting at 100 undo's
3192 * undo disabled - setting a limit of 0
3193 * undo limited - undo limit set to some to some number, like 2
3194 * bad input - sending a negative number should default to 100 undo's */
3196 HWND hwndRichEdit = new_richedit(NULL);
3197 CHARRANGE cr;
3198 int i;
3199 int result;
3201 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3202 cr.cpMin = 0;
3203 cr.cpMax = 1;
3204 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3205 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3206 also, multiple pastes don't combine like WM_CHAR would */
3207 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3209 /* first case - check the default */
3210 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3211 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3212 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3213 for (i=0; i<100; i++) /* Undo 100 of them */
3214 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3215 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3216 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3218 /* second case - cannot undo */
3219 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3220 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3221 SendMessage(hwndRichEdit,
3222 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3223 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3224 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3226 /* third case - set it to an arbitrary number */
3227 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3228 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3229 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3230 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3231 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3232 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3233 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3234 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3235 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3236 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3237 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3238 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3239 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3240 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3242 /* fourth case - setting negative numbers should default to 100 undos */
3243 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3244 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3245 ok (result == 100,
3246 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3248 DestroyWindow(hwndRichEdit);
3251 static void test_ES_PASSWORD(void)
3253 /* This isn't hugely testable, so we're just going to run it through its paces */
3255 HWND hwndRichEdit = new_richedit(NULL);
3256 WCHAR result;
3258 /* First, check the default of a regular control */
3259 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3260 ok (result == 0,
3261 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3263 /* Now, set it to something normal */
3264 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3265 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3266 ok (result == 120,
3267 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3269 /* Now, set it to something odd */
3270 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3271 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3272 ok (result == 1234,
3273 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3274 DestroyWindow(hwndRichEdit);
3277 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3278 LPBYTE pbBuff,
3279 LONG cb,
3280 LONG *pcb)
3282 char** str = (char**)dwCookie;
3283 *pcb = cb;
3284 if (*pcb > 0) {
3285 memcpy(*str, pbBuff, *pcb);
3286 *str += *pcb;
3288 return 0;
3291 static void test_WM_SETTEXT(void)
3293 HWND hwndRichEdit = new_richedit(NULL);
3294 const char * TestItem1 = "TestSomeText";
3295 const char * TestItem2 = "TestSomeText\r";
3296 const char * TestItem2_after = "TestSomeText\r\n";
3297 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3298 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3299 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3300 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3301 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3302 const char * TestItem5_after = "TestSomeText TestSomeText";
3303 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3304 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3305 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3306 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3308 const char rtftextA[] = "{\\rtf sometext}";
3309 const char urtftextA[] = "{\\urtf sometext}";
3310 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3311 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3312 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3314 char buf[1024] = {0};
3315 WCHAR bufW[1024] = {0};
3316 LRESULT result;
3318 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3319 any solitary \r to be converted to \r\n on return. Properly paired
3320 \r\n are not affected. It also shows that the special sequence \r\r\n
3321 gets converted to a single space.
3324 #define TEST_SETTEXT(a, b) \
3325 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3326 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3327 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3328 ok (result == lstrlen(buf), \
3329 "WM_GETTEXT returned %ld instead of expected %u\n", \
3330 result, lstrlen(buf)); \
3331 result = strcmp(b, buf); \
3332 ok(result == 0, \
3333 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3335 TEST_SETTEXT(TestItem1, TestItem1)
3336 TEST_SETTEXT(TestItem2, TestItem2_after)
3337 TEST_SETTEXT(TestItem3, TestItem3_after)
3338 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3339 TEST_SETTEXT(TestItem4, TestItem4_after)
3340 TEST_SETTEXT(TestItem5, TestItem5_after)
3341 TEST_SETTEXT(TestItem6, TestItem6_after)
3342 TEST_SETTEXT(TestItem7, TestItem7_after)
3344 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3345 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3346 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3347 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3348 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3349 DestroyWindow(hwndRichEdit);
3350 #undef TEST_SETTEXT
3352 #define TEST_SETTEXTW(a, b) \
3353 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3354 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3355 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3356 ok (result == lstrlenW(bufW), \
3357 "WM_GETTEXT returned %ld instead of expected %u\n", \
3358 result, lstrlenW(bufW)); \
3359 result = lstrcmpW(b, bufW); \
3360 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3362 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3363 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3364 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3365 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3366 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3367 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3368 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3369 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3370 DestroyWindow(hwndRichEdit);
3371 #undef TEST_SETTEXTW
3374 static void test_EM_STREAMOUT(void)
3376 HWND hwndRichEdit = new_richedit(NULL);
3377 int r;
3378 EDITSTREAM es;
3379 char buf[1024] = {0};
3380 char * p;
3382 const char * TestItem1 = "TestSomeText";
3383 const char * TestItem2 = "TestSomeText\r";
3384 const char * TestItem3 = "TestSomeText\r\n";
3386 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3387 p = buf;
3388 es.dwCookie = (DWORD_PTR)&p;
3389 es.dwError = 0;
3390 es.pfnCallback = test_WM_SETTEXT_esCallback;
3391 memset(buf, 0, sizeof(buf));
3392 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3393 r = strlen(buf);
3394 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3395 ok(strcmp(buf, TestItem1) == 0,
3396 "streamed text different, got %s\n", buf);
3398 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3399 p = buf;
3400 es.dwCookie = (DWORD_PTR)&p;
3401 es.dwError = 0;
3402 es.pfnCallback = test_WM_SETTEXT_esCallback;
3403 memset(buf, 0, sizeof(buf));
3404 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3405 r = strlen(buf);
3406 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3407 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3408 ok(strcmp(buf, TestItem3) == 0,
3409 "streamed text different from, got %s\n", buf);
3410 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3411 p = buf;
3412 es.dwCookie = (DWORD_PTR)&p;
3413 es.dwError = 0;
3414 es.pfnCallback = test_WM_SETTEXT_esCallback;
3415 memset(buf, 0, sizeof(buf));
3416 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3417 r = strlen(buf);
3418 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3419 ok(strcmp(buf, TestItem3) == 0,
3420 "streamed text different, got %s\n", buf);
3422 DestroyWindow(hwndRichEdit);
3425 static void test_EM_STREAMOUT_FONTTBL(void)
3427 HWND hwndRichEdit = new_richedit(NULL);
3428 EDITSTREAM es;
3429 char buf[1024] = {0};
3430 char * p;
3431 char * fontTbl;
3432 int brackCount;
3434 const char * TestItem = "TestSomeText";
3436 /* fills in the richedit control with some text */
3437 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3439 /* streams out the text in rtf format */
3440 p = buf;
3441 es.dwCookie = (DWORD_PTR)&p;
3442 es.dwError = 0;
3443 es.pfnCallback = test_WM_SETTEXT_esCallback;
3444 memset(buf, 0, sizeof(buf));
3445 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3447 /* scans for \fonttbl, error if not found */
3448 fontTbl = strstr(buf, "\\fonttbl");
3449 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3450 if(fontTbl)
3452 /* scans for terminating closing bracket */
3453 brackCount = 1;
3454 while(*fontTbl && brackCount)
3456 if(*fontTbl == '{')
3457 brackCount++;
3458 else if(*fontTbl == '}')
3459 brackCount--;
3460 fontTbl++;
3462 /* checks whether closing bracket is ok */
3463 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3464 if(!brackCount)
3466 /* char before closing fonttbl block should be a closed bracket */
3467 fontTbl -= 2;
3468 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3470 /* char after fonttbl block should be a crlf */
3471 fontTbl += 2;
3472 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3475 DestroyWindow(hwndRichEdit);
3479 static void test_EM_SETTEXTEX(void)
3481 HWND hwndRichEdit, parent;
3482 SCROLLINFO si;
3483 int sel_start, sel_end;
3484 SETTEXTEX setText;
3485 GETTEXTEX getText;
3486 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3487 'S', 'o', 'm', 'e',
3488 'T', 'e', 'x', 't', 0};
3489 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3490 't', 'S', 'o', 'm',
3491 'e', 'T', 'e', 'x',
3492 't', 't', 'S', 'o',
3493 'm', 'e', 'T', 'e',
3494 'x', 't', 0};
3495 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3496 '\r','t','S','o','m','e','T','e','x','t',0};
3497 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3498 'S', 'o', 'm', 'e',
3499 'T', 'e', 'x', 't',
3500 '\r', 0};
3501 const char * TestItem2_after = "TestSomeText\r\n";
3502 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3503 'S', 'o', 'm', 'e',
3504 'T', 'e', 'x', 't',
3505 '\r','\n','\r','\n', 0};
3506 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3507 'S', 'o', 'm', 'e',
3508 'T', 'e', 'x', 't',
3509 '\n','\n', 0};
3510 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3511 'S', 'o', 'm', 'e',
3512 'T', 'e', 'x', 't',
3513 '\r','\r', 0};
3514 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3515 'S', 'o', 'm', 'e',
3516 'T', 'e', 'x', 't',
3517 '\r','\r','\n','\r',
3518 '\n', 0};
3519 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3520 'S', 'o', 'm', 'e',
3521 'T', 'e', 'x', 't',
3522 ' ','\r', 0};
3523 #define MAX_BUF_LEN 1024
3524 WCHAR buf[MAX_BUF_LEN];
3525 char bufACP[MAX_BUF_LEN];
3526 char * p;
3527 int result;
3528 CHARRANGE cr;
3529 EDITSTREAM es;
3530 WNDCLASSA cls;
3532 /* Test the scroll position with and without a parent window.
3534 * For some reason the scroll position is 0 after EM_SETTEXTEX
3535 * with the ST_SELECTION flag only when the control has a parent
3536 * window, even though the selection is at the end. */
3537 cls.style = 0;
3538 cls.lpfnWndProc = DefWindowProcA;
3539 cls.cbClsExtra = 0;
3540 cls.cbWndExtra = 0;
3541 cls.hInstance = GetModuleHandleA(0);
3542 cls.hIcon = 0;
3543 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3544 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3545 cls.lpszMenuName = NULL;
3546 cls.lpszClassName = "ParentTestClass";
3547 if(!RegisterClassA(&cls)) assert(0);
3549 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3550 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3551 ok (parent != 0, "Failed to create parent window\n");
3553 hwndRichEdit = CreateWindowEx(0,
3554 RICHEDIT_CLASS, NULL,
3555 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3556 0, 0, 200, 60, parent, NULL,
3557 hmoduleRichEdit, NULL);
3559 setText.codepage = CP_ACP;
3560 setText.flags = ST_SELECTION;
3561 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3562 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3563 si.cbSize = sizeof(si);
3564 si.fMask = SIF_ALL;
3565 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3566 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3567 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3568 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3569 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3571 DestroyWindow(parent);
3573 /* Test without a parent window */
3574 hwndRichEdit = new_richedit(NULL);
3575 setText.codepage = CP_ACP;
3576 setText.flags = ST_SELECTION;
3577 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3578 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3579 si.cbSize = sizeof(si);
3580 si.fMask = SIF_ALL;
3581 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3582 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3583 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3584 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3585 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3587 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3588 * but this time it is because the selection is at the beginning. */
3589 setText.codepage = CP_ACP;
3590 setText.flags = ST_DEFAULT;
3591 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3592 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3593 si.cbSize = sizeof(si);
3594 si.fMask = SIF_ALL;
3595 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3596 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3597 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3598 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3599 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3601 setText.codepage = 1200; /* no constant for unicode */
3602 getText.codepage = 1200; /* no constant for unicode */
3603 getText.cb = MAX_BUF_LEN;
3604 getText.flags = GT_DEFAULT;
3605 getText.lpDefaultChar = NULL;
3606 getText.lpUsedDefChar = NULL;
3608 setText.flags = 0;
3609 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3610 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3611 ok(lstrcmpW(buf, TestItem1) == 0,
3612 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3614 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3615 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3617 setText.codepage = 1200; /* no constant for unicode */
3618 getText.codepage = 1200; /* no constant for unicode */
3619 getText.cb = MAX_BUF_LEN;
3620 getText.flags = GT_DEFAULT;
3621 getText.lpDefaultChar = NULL;
3622 getText.lpUsedDefChar = NULL;
3623 setText.flags = 0;
3624 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3625 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3626 ok(lstrcmpW(buf, TestItem2) == 0,
3627 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3629 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3630 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3631 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3632 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3634 /* Baseline test for just-enough buffer space for string */
3635 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3636 getText.codepage = 1200; /* no constant for unicode */
3637 getText.flags = GT_DEFAULT;
3638 getText.lpDefaultChar = NULL;
3639 getText.lpUsedDefChar = NULL;
3640 memset(buf, 0, MAX_BUF_LEN);
3641 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3642 ok(lstrcmpW(buf, TestItem2) == 0,
3643 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3645 /* When there is enough space for one character, but not both, of the CRLF
3646 pair at the end of the string, the CR is not copied at all. That is,
3647 the caller must not see CRLF pairs truncated to CR at the end of the
3648 string.
3650 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3651 getText.codepage = 1200; /* no constant for unicode */
3652 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3653 getText.lpDefaultChar = NULL;
3654 getText.lpUsedDefChar = NULL;
3655 memset(buf, 0, MAX_BUF_LEN);
3656 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3657 ok(lstrcmpW(buf, TestItem1) == 0,
3658 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3661 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3662 setText.codepage = 1200; /* no constant for unicode */
3663 getText.codepage = 1200; /* no constant for unicode */
3664 getText.cb = MAX_BUF_LEN;
3665 getText.flags = GT_DEFAULT;
3666 getText.lpDefaultChar = NULL;
3667 getText.lpUsedDefChar = NULL;
3668 setText.flags = 0;
3669 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3670 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3671 ok(lstrcmpW(buf, TestItem3_after) == 0,
3672 "EM_SETTEXTEX did not convert properly\n");
3674 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3675 setText.codepage = 1200; /* no constant for unicode */
3676 getText.codepage = 1200; /* no constant for unicode */
3677 getText.cb = MAX_BUF_LEN;
3678 getText.flags = GT_DEFAULT;
3679 getText.lpDefaultChar = NULL;
3680 getText.lpUsedDefChar = NULL;
3681 setText.flags = 0;
3682 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3683 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3684 ok(lstrcmpW(buf, TestItem3_after) == 0,
3685 "EM_SETTEXTEX did not convert properly\n");
3687 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3688 setText.codepage = 1200; /* no constant for unicode */
3689 getText.codepage = 1200; /* no constant for unicode */
3690 getText.cb = MAX_BUF_LEN;
3691 getText.flags = GT_DEFAULT;
3692 getText.lpDefaultChar = NULL;
3693 getText.lpUsedDefChar = NULL;
3694 setText.flags = 0;
3695 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3696 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3697 ok(lstrcmpW(buf, TestItem4_after) == 0,
3698 "EM_SETTEXTEX did not convert properly\n");
3700 /* !ST_SELECTION && Unicode && !\rtf */
3701 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3702 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3704 ok (result == 1,
3705 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3706 ok(lstrlenW(buf) == 0,
3707 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3709 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3710 setText.flags = 0;
3711 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3712 /* select some text */
3713 cr.cpMax = 1;
3714 cr.cpMin = 3;
3715 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3716 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3717 setText.flags = ST_SELECTION;
3718 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3719 ok(result == 0,
3720 "EM_SETTEXTEX with NULL lParam to replace selection"
3721 " with no text should return 0. Got %i\n",
3722 result);
3724 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3725 setText.flags = 0;
3726 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3727 /* select some text */
3728 cr.cpMax = 1;
3729 cr.cpMin = 3;
3730 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3731 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3732 setText.flags = ST_SELECTION;
3733 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3734 (WPARAM)&setText, (LPARAM) TestItem1);
3735 /* get text */
3736 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3737 ok(result == lstrlenW(TestItem1),
3738 "EM_SETTEXTEX with NULL lParam to replace selection"
3739 " with no text should return 0. Got %i\n",
3740 result);
3741 ok(lstrlenW(buf) == 22,
3742 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3743 lstrlenW(buf) );
3745 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3746 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3747 p = (char *)buf;
3748 es.dwCookie = (DWORD_PTR)&p;
3749 es.dwError = 0;
3750 es.pfnCallback = test_WM_SETTEXT_esCallback;
3751 memset(buf, 0, sizeof(buf));
3752 SendMessage(hwndRichEdit, EM_STREAMOUT,
3753 (WPARAM)(SF_RTF), (LPARAM)&es);
3754 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3756 /* !ST_SELECTION && !Unicode && \rtf */
3757 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3758 getText.codepage = 1200; /* no constant for unicode */
3759 getText.cb = MAX_BUF_LEN;
3760 getText.flags = GT_DEFAULT;
3761 getText.lpDefaultChar = NULL;
3762 getText.lpUsedDefChar = NULL;
3764 setText.flags = 0;
3765 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3766 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3767 ok(lstrcmpW(buf, TestItem1) == 0,
3768 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3770 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3771 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3772 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3773 getText.codepage = CP_ACP;
3774 getText.cb = MAX_BUF_LEN;
3775 getText.flags = GT_DEFAULT;
3776 getText.lpDefaultChar = NULL;
3777 getText.lpUsedDefChar = NULL;
3779 setText.flags = ST_SELECTION;
3780 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3781 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3782 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3783 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3784 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3786 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3787 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3788 p = (char *)buf;
3789 es.dwCookie = (DWORD_PTR)&p;
3790 es.dwError = 0;
3791 es.pfnCallback = test_WM_SETTEXT_esCallback;
3792 memset(buf, 0, sizeof(buf));
3793 SendMessage(hwndRichEdit, EM_STREAMOUT,
3794 (WPARAM)(SF_RTF), (LPARAM)&es);
3795 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3797 /* select some text */
3798 cr.cpMax = 1;
3799 cr.cpMin = 3;
3800 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3802 /* ST_SELECTION && !Unicode && \rtf */
3803 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3804 getText.codepage = 1200; /* no constant for unicode */
3805 getText.cb = MAX_BUF_LEN;
3806 getText.flags = GT_DEFAULT;
3807 getText.lpDefaultChar = NULL;
3808 getText.lpUsedDefChar = NULL;
3810 setText.flags = ST_SELECTION;
3811 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3812 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3813 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3815 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3816 setText.codepage = 1200; /* no constant for unicode */
3817 getText.codepage = CP_ACP;
3818 getText.cb = MAX_BUF_LEN;
3820 setText.flags = 0;
3821 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3822 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3824 /* select some text */
3825 cr.cpMax = 1;
3826 cr.cpMin = 3;
3827 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3829 /* ST_SELECTION && !Unicode && !\rtf */
3830 setText.codepage = CP_ACP;
3831 getText.codepage = 1200; /* no constant for unicode */
3832 getText.cb = MAX_BUF_LEN;
3833 getText.flags = GT_DEFAULT;
3834 getText.lpDefaultChar = NULL;
3835 getText.lpUsedDefChar = NULL;
3837 setText.flags = ST_SELECTION;
3838 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3839 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3840 ok(lstrcmpW(buf, TestItem1alt) == 0,
3841 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3842 " using ST_SELECTION and non-Unicode\n");
3844 /* Test setting text using rich text format */
3845 setText.flags = 0;
3846 setText.codepage = CP_ACP;
3847 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3848 getText.codepage = CP_ACP;
3849 getText.cb = MAX_BUF_LEN;
3850 getText.flags = GT_DEFAULT;
3851 getText.lpDefaultChar = NULL;
3852 getText.lpUsedDefChar = NULL;
3853 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3854 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3856 setText.flags = 0;
3857 setText.codepage = CP_ACP;
3858 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3859 getText.codepage = CP_ACP;
3860 getText.cb = MAX_BUF_LEN;
3861 getText.flags = GT_DEFAULT;
3862 getText.lpDefaultChar = NULL;
3863 getText.lpUsedDefChar = NULL;
3864 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3865 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3867 DestroyWindow(hwndRichEdit);
3870 static void test_EM_LIMITTEXT(void)
3872 int ret;
3874 HWND hwndRichEdit = new_richedit(NULL);
3876 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3877 * about setting the length to -1 for multiline edit controls doesn't happen.
3880 /* Don't check default gettextlimit case. That's done in other tests */
3882 /* Set textlimit to 100 */
3883 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3884 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3885 ok (ret == 100,
3886 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3888 /* Set textlimit to 0 */
3889 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3890 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3891 ok (ret == 65536,
3892 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3894 /* Set textlimit to -1 */
3895 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3896 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3897 ok (ret == -1,
3898 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3900 /* Set textlimit to -2 */
3901 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3902 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3903 ok (ret == -2,
3904 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3906 DestroyWindow (hwndRichEdit);
3910 static void test_EM_EXLIMITTEXT(void)
3912 int i, selBegin, selEnd, len1, len2;
3913 int result;
3914 char text[1024 + 1];
3915 char buffer[1024 + 1];
3916 int textlimit = 0; /* multiple of 100 */
3917 HWND hwndRichEdit = new_richedit(NULL);
3919 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3920 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3922 textlimit = 256000;
3923 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3924 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3925 /* set higher */
3926 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3928 textlimit = 1000;
3929 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3930 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3931 /* set lower */
3932 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3934 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3935 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3936 /* default for WParam = 0 */
3937 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3939 textlimit = sizeof(text)-1;
3940 memset(text, 'W', textlimit);
3941 text[sizeof(text)-1] = 0;
3942 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3943 /* maxed out text */
3944 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3946 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3947 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3948 len1 = selEnd - selBegin;
3950 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3951 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3952 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3953 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3954 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3955 len2 = selEnd - selBegin;
3957 ok(len1 != len2,
3958 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3959 len1,len2,i);
3961 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3962 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3963 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3964 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3965 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3966 len1 = selEnd - selBegin;
3968 ok(len1 != len2,
3969 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3970 len1,len2,i);
3972 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3973 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3974 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3975 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3976 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3977 len2 = selEnd - selBegin;
3979 ok(len1 == len2,
3980 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3981 len1,len2,i);
3983 /* set text up to the limit, select all the text, then add a char */
3984 textlimit = 5;
3985 memset(text, 'W', textlimit);
3986 text[textlimit] = 0;
3987 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3988 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3989 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3990 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3991 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3992 result = strcmp(buffer, "A");
3993 ok(0 == result, "got string = \"%s\"\n", buffer);
3995 /* WM_SETTEXT not limited */
3996 textlimit = 10;
3997 memset(text, 'W', textlimit);
3998 text[textlimit] = 0;
3999 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4000 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4001 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4002 i = strlen(buffer);
4003 ok(10 == i, "expected 10 chars\n");
4004 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4005 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4007 /* try inserting more text at end */
4008 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4009 ok(0 == i, "WM_CHAR wasn't processed\n");
4010 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4011 i = strlen(buffer);
4012 ok(10 == i, "expected 10 chars, got %i\n", i);
4013 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4014 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4016 /* try inserting text at beginning */
4017 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4018 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4019 ok(0 == i, "WM_CHAR wasn't processed\n");
4020 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4021 i = strlen(buffer);
4022 ok(10 == i, "expected 10 chars, got %i\n", i);
4023 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4024 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4026 /* WM_CHAR is limited */
4027 textlimit = 1;
4028 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4029 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4030 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4031 ok(0 == i, "WM_CHAR wasn't processed\n");
4032 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4033 ok(0 == i, "WM_CHAR wasn't processed\n");
4034 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4035 i = strlen(buffer);
4036 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4038 DestroyWindow(hwndRichEdit);
4041 static void test_EM_GETLIMITTEXT(void)
4043 int i;
4044 HWND hwndRichEdit = new_richedit(NULL);
4046 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4047 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4049 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4050 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4051 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4053 DestroyWindow(hwndRichEdit);
4056 static void test_WM_SETFONT(void)
4058 /* There is no invalid input or error conditions for this function.
4059 * NULL wParam and lParam just fall back to their default values
4060 * It should be noted that even if you use a gibberish name for your fonts
4061 * here, it will still work because the name is stored. They will display as
4062 * System, but will report their name to be whatever they were created as */
4064 HWND hwndRichEdit = new_richedit(NULL);
4065 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4066 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4067 FF_DONTCARE, "Marlett");
4068 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4069 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4070 FF_DONTCARE, "MS Sans Serif");
4071 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4072 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4073 FF_DONTCARE, "Courier");
4074 LOGFONTA sentLogFont;
4075 CHARFORMAT2A returnedCF2A;
4077 returnedCF2A.cbSize = sizeof(returnedCF2A);
4079 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4080 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4081 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4083 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4084 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4085 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4086 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4088 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4089 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4090 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4091 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4092 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4093 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4095 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4096 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4097 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4098 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4099 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4100 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4102 /* This last test is special since we send in NULL. We clear the variables
4103 * and just compare to "System" instead of the sent in font name. */
4104 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4105 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4106 returnedCF2A.cbSize = sizeof(returnedCF2A);
4108 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4109 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4110 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4111 ok (!strcmp("System",returnedCF2A.szFaceName),
4112 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4114 DestroyWindow(hwndRichEdit);
4118 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4119 LPBYTE pbBuff,
4120 LONG cb,
4121 LONG *pcb)
4123 const char** str = (const char**)dwCookie;
4124 int size = strlen(*str);
4125 if(size > 3) /* let's make it piecemeal for fun */
4126 size = 3;
4127 *pcb = cb;
4128 if (*pcb > size) {
4129 *pcb = size;
4131 if (*pcb > 0) {
4132 memcpy(pbBuff, *str, *pcb);
4133 *str += *pcb;
4135 return 0;
4138 static void test_EM_GETMODIFY(void)
4140 HWND hwndRichEdit = new_richedit(NULL);
4141 LRESULT result;
4142 SETTEXTEX setText;
4143 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4144 'S', 'o', 'm', 'e',
4145 'T', 'e', 'x', 't', 0};
4146 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4147 'S', 'o', 'm', 'e',
4148 'O', 't', 'h', 'e', 'r',
4149 'T', 'e', 'x', 't', 0};
4150 const char* streamText = "hello world";
4151 CHARFORMAT2 cf2;
4152 PARAFORMAT2 pf2;
4153 EDITSTREAM es;
4155 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4156 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4157 FF_DONTCARE, "Courier");
4159 setText.codepage = 1200; /* no constant for unicode */
4160 setText.flags = ST_KEEPUNDO;
4163 /* modify flag shouldn't be set when richedit is first created */
4164 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4165 ok (result == 0,
4166 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4168 /* setting modify flag should actually set it */
4169 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4170 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4171 ok (result != 0,
4172 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4174 /* clearing modify flag should actually clear it */
4175 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4176 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4177 ok (result == 0,
4178 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4180 /* setting font doesn't change modify flag */
4181 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4182 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4183 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4184 ok (result == 0,
4185 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4187 /* setting text should set modify flag */
4188 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4189 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4190 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4191 ok (result != 0,
4192 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4194 /* undo previous text doesn't reset modify flag */
4195 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4196 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4197 ok (result != 0,
4198 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4200 /* set text with no flag to keep undo stack should not set modify flag */
4201 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4202 setText.flags = 0;
4203 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4204 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4205 ok (result == 0,
4206 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4208 /* WM_SETTEXT doesn't modify */
4209 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4210 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4211 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4212 ok (result == 0,
4213 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4215 /* clear the text */
4216 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4217 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4218 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4219 ok (result == 0,
4220 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4222 /* replace text */
4223 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4224 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4225 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4226 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4227 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4228 ok (result != 0,
4229 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4231 /* copy/paste text 1 */
4232 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4233 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4234 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4235 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4236 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4237 ok (result != 0,
4238 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4240 /* copy/paste text 2 */
4241 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4242 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4243 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4244 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4245 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4246 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4247 ok (result != 0,
4248 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4250 /* press char */
4251 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4252 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4253 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4254 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4255 ok (result != 0,
4256 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4258 /* press del */
4259 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4260 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4261 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4262 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4263 ok (result != 0,
4264 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4266 /* set char format */
4267 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4268 cf2.cbSize = sizeof(CHARFORMAT2);
4269 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4270 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4271 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4272 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4273 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4274 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4275 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4276 ok (result != 0,
4277 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4279 /* set para format */
4280 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4281 pf2.cbSize = sizeof(PARAFORMAT2);
4282 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4283 (LPARAM) &pf2);
4284 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4285 pf2.wAlignment = PFA_RIGHT;
4286 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4287 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4288 ok (result == 0,
4289 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4291 /* EM_STREAM */
4292 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4293 es.dwCookie = (DWORD_PTR)&streamText;
4294 es.dwError = 0;
4295 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4296 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4297 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4298 ok (result != 0,
4299 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4301 DestroyWindow(hwndRichEdit);
4304 struct exsetsel_s {
4305 LONG min;
4306 LONG max;
4307 LRESULT expected_retval;
4308 int expected_getsel_start;
4309 int expected_getsel_end;
4310 int _getsel_todo_wine;
4313 const struct exsetsel_s exsetsel_tests[] = {
4314 /* sanity tests */
4315 {5, 10, 10, 5, 10, 0},
4316 {15, 17, 17, 15, 17, 0},
4317 /* test cpMax > strlen() */
4318 {0, 100, 18, 0, 18, 1},
4319 /* test cpMin == cpMax */
4320 {5, 5, 5, 5, 5, 0},
4321 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4322 {-1, 0, 5, 5, 5, 0},
4323 {-1, 17, 5, 5, 5, 0},
4324 {-1, 18, 5, 5, 5, 0},
4325 /* test cpMin < 0 && cpMax < 0 */
4326 {-1, -1, 17, 17, 17, 0},
4327 {-4, -5, 17, 17, 17, 0},
4328 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4329 {0, -1, 18, 0, 18, 1},
4330 {17, -5, 18, 17, 18, 1},
4331 {18, -3, 17, 17, 17, 0},
4332 /* test if cpMin > cpMax */
4333 {15, 19, 18, 15, 18, 1},
4334 {19, 15, 18, 15, 18, 1}
4337 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4338 CHARRANGE cr;
4339 LRESULT result;
4340 int start, end;
4342 cr.cpMin = setsel->min;
4343 cr.cpMax = setsel->max;
4344 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4346 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4348 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4350 if (setsel->_getsel_todo_wine) {
4351 todo_wine {
4352 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);
4354 } else {
4355 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);
4359 static void test_EM_EXSETSEL(void)
4361 HWND hwndRichEdit = new_richedit(NULL);
4362 int i;
4363 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4365 /* sending some text to the window */
4366 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4367 /* 01234567890123456*/
4368 /* 10 */
4370 for (i = 0; i < num_tests; i++) {
4371 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4374 DestroyWindow(hwndRichEdit);
4377 static void test_EM_REPLACESEL(int redraw)
4379 HWND hwndRichEdit = new_richedit(NULL);
4380 char buffer[1024] = {0};
4381 int r;
4382 GETTEXTEX getText;
4383 CHARRANGE cr;
4385 /* sending some text to the window */
4386 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4387 /* 01234567890123456*/
4388 /* 10 */
4390 /* FIXME add more tests */
4391 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4392 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4393 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4394 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4395 r = strcmp(buffer, "testing");
4396 ok(0 == r, "expected %d, got %d\n", 0, r);
4398 DestroyWindow(hwndRichEdit);
4400 hwndRichEdit = new_richedit(NULL);
4402 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4403 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4405 /* Test behavior with carriage returns and newlines */
4406 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4407 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4408 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4409 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4410 r = strcmp(buffer, "RichEdit1");
4411 ok(0 == r, "expected %d, got %d\n", 0, r);
4412 getText.cb = 1024;
4413 getText.codepage = CP_ACP;
4414 getText.flags = GT_DEFAULT;
4415 getText.lpDefaultChar = NULL;
4416 getText.lpUsedDefChar = NULL;
4417 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4418 ok(strcmp(buffer, "RichEdit1") == 0,
4419 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4421 /* Test number of lines reported after EM_REPLACESEL */
4422 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4423 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4425 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4426 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4427 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4428 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4429 r = strcmp(buffer, "RichEdit1\r\n");
4430 ok(0 == r, "expected %d, got %d\n", 0, r);
4431 getText.cb = 1024;
4432 getText.codepage = CP_ACP;
4433 getText.flags = GT_DEFAULT;
4434 getText.lpDefaultChar = NULL;
4435 getText.lpUsedDefChar = NULL;
4436 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4437 ok(strcmp(buffer, "RichEdit1\r") == 0,
4438 "EM_GETTEXTEX returned incorrect string\n");
4440 /* Test number of lines reported after EM_REPLACESEL */
4441 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4442 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4444 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4445 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4446 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4448 /* Test number of lines reported after EM_REPLACESEL */
4449 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4450 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4452 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4453 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4454 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4455 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4457 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4458 r = strcmp(buffer, "RichEdit1\r\n");
4459 ok(0 == r, "expected %d, got %d\n", 0, r);
4460 getText.cb = 1024;
4461 getText.codepage = CP_ACP;
4462 getText.flags = GT_DEFAULT;
4463 getText.lpDefaultChar = NULL;
4464 getText.lpUsedDefChar = NULL;
4465 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4466 ok(strcmp(buffer, "RichEdit1\r") == 0,
4467 "EM_GETTEXTEX returned incorrect string\n");
4469 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4470 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4471 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4472 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4474 /* The following tests show that richedit should handle the special \r\r\n
4475 sequence by turning it into a single space on insertion. However,
4476 EM_REPLACESEL on WinXP returns the number of characters in the original
4477 string.
4480 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4481 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4482 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4483 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4484 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4485 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4486 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4488 /* Test the actual string */
4489 getText.cb = 1024;
4490 getText.codepage = CP_ACP;
4491 getText.flags = GT_DEFAULT;
4492 getText.lpDefaultChar = NULL;
4493 getText.lpUsedDefChar = NULL;
4494 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4495 ok(strcmp(buffer, "\r\r") == 0,
4496 "EM_GETTEXTEX returned incorrect string\n");
4498 /* Test number of lines reported after EM_REPLACESEL */
4499 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4500 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4502 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4503 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4504 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4505 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4506 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4507 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4508 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4510 /* Test the actual string */
4511 getText.cb = 1024;
4512 getText.codepage = CP_ACP;
4513 getText.flags = GT_DEFAULT;
4514 getText.lpDefaultChar = NULL;
4515 getText.lpUsedDefChar = NULL;
4516 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4517 ok(strcmp(buffer, " ") == 0,
4518 "EM_GETTEXTEX returned incorrect string\n");
4520 /* Test number of lines reported after EM_REPLACESEL */
4521 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4522 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4524 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4525 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4526 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\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(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4549 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4550 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4551 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4552 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4554 /* Test the actual string */
4555 getText.cb = 1024;
4556 getText.codepage = CP_ACP;
4557 getText.flags = GT_DEFAULT;
4558 getText.lpDefaultChar = NULL;
4559 getText.lpUsedDefChar = NULL;
4560 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4561 ok(strcmp(buffer, " \r") == 0,
4562 "EM_GETTEXTEX returned incorrect string\n");
4564 /* Test number of lines reported after EM_REPLACESEL */
4565 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4566 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4568 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4569 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4570 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4571 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4572 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4573 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4574 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4576 /* Test the actual string */
4577 getText.cb = 1024;
4578 getText.codepage = CP_ACP;
4579 getText.flags = GT_DEFAULT;
4580 getText.lpDefaultChar = NULL;
4581 getText.lpUsedDefChar = NULL;
4582 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4583 ok(strcmp(buffer, " \r\r") == 0,
4584 "EM_GETTEXTEX returned incorrect string\n");
4586 /* Test number of lines reported after EM_REPLACESEL */
4587 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4588 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4590 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4591 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4592 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4593 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4594 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4595 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4596 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4598 /* Test the actual string */
4599 getText.cb = 1024;
4600 getText.codepage = CP_ACP;
4601 getText.flags = GT_DEFAULT;
4602 getText.lpDefaultChar = NULL;
4603 getText.lpUsedDefChar = NULL;
4604 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4605 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4606 "EM_GETTEXTEX returned incorrect string\n");
4608 /* Test number of lines reported after EM_REPLACESEL */
4609 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4610 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4612 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4613 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4614 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4615 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4616 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4617 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4618 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4620 /* Test the actual string */
4621 getText.cb = 1024;
4622 getText.codepage = CP_ACP;
4623 getText.flags = GT_DEFAULT;
4624 getText.lpDefaultChar = NULL;
4625 getText.lpUsedDefChar = NULL;
4626 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4627 ok(strcmp(buffer, "\r\r") == 0,
4628 "EM_GETTEXTEX returned incorrect string\n");
4630 /* Test number of lines reported after EM_REPLACESEL */
4631 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4632 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4634 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4635 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4636 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4637 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4638 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4639 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4640 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4642 /* Test the actual string */
4643 getText.cb = 1024;
4644 getText.codepage = CP_ACP;
4645 getText.flags = GT_DEFAULT;
4646 getText.lpDefaultChar = NULL;
4647 getText.lpUsedDefChar = NULL;
4648 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4649 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4650 "EM_GETTEXTEX returned incorrect string\n");
4652 /* Test number of lines reported after EM_REPLACESEL */
4653 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4654 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4656 if (!redraw)
4657 /* This is needed to avoid interferring with keybd_event calls
4658 * on other tests that simulate keyboard events. */
4659 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4661 DestroyWindow(hwndRichEdit);
4664 static void test_WM_PASTE(void)
4666 int result;
4667 char buffer[1024] = {0};
4668 const char* text1 = "testing paste\r";
4669 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4670 const char* text1_after = "testing paste\r\n";
4671 const char* text2 = "testing paste\r\rtesting paste";
4672 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4673 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4674 HWND hwndRichEdit = new_richedit(NULL);
4676 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4677 * to test the state of the modifiers (Ctrl/Alt/Shift).
4679 * Therefore Ctrl-<key> keystrokes need to be simulated with
4680 * keybd_event or by using SetKeyboardState to set the modifiers
4681 * and SendMessage to simulate the keystrokes.
4684 /* Sent keystrokes with keybd_event */
4685 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4686 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4687 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4688 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4689 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4691 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4692 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4694 SEND_CTRL_C(hwndRichEdit); /* Copy */
4695 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4696 SEND_CTRL_V(hwndRichEdit); /* Paste */
4697 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4698 /* Pasted text should be visible at this step */
4699 result = strcmp(text1_step1, buffer);
4700 ok(result == 0,
4701 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4703 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4704 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4705 /* Text should be the same as before (except for \r -> \r\n conversion) */
4706 result = strcmp(text1_after, buffer);
4707 ok(result == 0,
4708 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4710 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4711 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4712 SEND_CTRL_C(hwndRichEdit); /* Copy */
4713 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4714 SEND_CTRL_V(hwndRichEdit); /* Paste */
4715 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4716 /* Pasted text should be visible at this step */
4717 result = strcmp(text3, buffer);
4718 ok(result == 0,
4719 "test paste: strcmp = %i\n", result);
4720 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4721 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4722 /* Text should be the same as before (except for \r -> \r\n conversion) */
4723 result = strcmp(text2_after, buffer);
4724 ok(result == 0,
4725 "test paste: strcmp = %i\n", result);
4726 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4727 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4728 /* Text should revert to post-paste state */
4729 result = strcmp(buffer,text3);
4730 ok(result == 0,
4731 "test paste: strcmp = %i\n", result);
4733 #undef SEND_CTRL_C
4734 #undef SEND_CTRL_X
4735 #undef SEND_CTRL_V
4736 #undef SEND_CTRL_Z
4737 #undef SEND_CTRL_Y
4739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4740 /* Send WM_CHAR to simulates Ctrl-V */
4741 SendMessage(hwndRichEdit, WM_CHAR, 22,
4742 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4743 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4744 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4745 result = strcmp(buffer,"");
4746 ok(result == 0,
4747 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4749 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4750 * with SetKeyboard state. */
4752 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4753 /* Simulates paste (Ctrl-V) */
4754 hold_key(VK_CONTROL);
4755 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4756 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4757 release_key(VK_CONTROL);
4758 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4759 result = strcmp(buffer,"paste");
4760 ok(result == 0,
4761 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4763 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4764 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4765 /* Simulates copy (Ctrl-C) */
4766 hold_key(VK_CONTROL);
4767 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4768 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) | 1);
4769 release_key(VK_CONTROL);
4770 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4771 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4772 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4773 result = strcmp(buffer,"testing");
4774 ok(result == 0,
4775 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4777 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4778 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4779 /* Simulates select all (Ctrl-A) */
4780 hold_key(VK_CONTROL);
4781 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4782 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) | 1);
4783 /* Simulates select cut (Ctrl-X) */
4784 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4785 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) | 1);
4786 release_key(VK_CONTROL);
4787 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4788 result = strcmp(buffer,"");
4789 ok(result == 0,
4790 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4791 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4792 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4793 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4794 result = strcmp(buffer,"cut\r\n");
4795 todo_wine ok(result == 0,
4796 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4797 /* Simulates undo (Ctrl-Z) */
4798 hold_key(VK_CONTROL);
4799 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4800 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4801 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4802 result = strcmp(buffer,"");
4803 ok(result == 0,
4804 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4805 /* Simulates redo (Ctrl-Y) */
4806 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4807 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4808 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4809 result = strcmp(buffer,"cut\r\n");
4810 todo_wine ok(result == 0,
4811 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4812 release_key(VK_CONTROL);
4814 DestroyWindow(hwndRichEdit);
4817 static void test_EM_FORMATRANGE(void)
4819 int r, i, tpp_x, tpp_y;
4820 HDC hdc;
4821 HWND hwndRichEdit = new_richedit(NULL);
4822 FORMATRANGE fr;
4823 BOOL skip_non_english;
4824 static const struct {
4825 const char *string; /* The string */
4826 int first; /* First 'pagebreak', 0 for don't care */
4827 int second; /* Second 'pagebreak', 0 for don't care */
4828 } fmtstrings[] = {
4829 {"WINE wine", 0, 0},
4830 {"WINE wineWine", 0, 0},
4831 {"WINE\r\nwine\r\nwine", 5, 10},
4832 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4833 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4836 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4837 if (skip_non_english)
4838 skip("Skipping some tests on non-English platform\n");
4840 hdc = GetDC(hwndRichEdit);
4841 ok(hdc != NULL, "Could not get HDC\n");
4843 /* Calculate the twips per pixel */
4844 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4845 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4847 /* Test the simple case where all the text fits in the page rect. */
4848 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4849 fr.hdc = fr.hdcTarget = hdc;
4850 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4851 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4852 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4853 fr.chrg.cpMin = 0;
4854 fr.chrg.cpMax = -1;
4855 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4856 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4858 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4859 fr.rc.bottom = fr.rcPage.bottom;
4860 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4861 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4863 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4865 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4867 GETTEXTLENGTHEX gtl;
4868 SIZE stringsize;
4869 int len;
4871 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4873 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4874 gtl.codepage = CP_ACP;
4875 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4877 /* Get some size information for the string */
4878 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4880 /* Define the box to be half the width needed and a bit larger than the height.
4881 * Changes to the width means we have at least 2 pages. Changes to the height
4882 * is done so we can check the changing of fr.rc.bottom.
4884 fr.hdc = fr.hdcTarget = hdc;
4885 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4886 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4887 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4889 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4890 todo_wine {
4891 ok(r == len, "Expected %d, got %d\n", len, r);
4894 /* We know that the page can't hold the full string. See how many characters
4895 * are on the first one
4897 fr.chrg.cpMin = 0;
4898 fr.chrg.cpMax = -1;
4899 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4900 todo_wine {
4901 if (! skip_non_english)
4902 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4904 if (fmtstrings[i].first)
4905 todo_wine {
4906 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4908 else
4909 ok(r < len, "Expected < %d, got %d\n", len, r);
4911 /* Do another page */
4912 fr.chrg.cpMin = r;
4913 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4914 if (fmtstrings[i].second)
4915 todo_wine {
4916 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4918 else if (! skip_non_english)
4919 ok (r < len, "Expected < %d, got %d\n", len, r);
4921 /* There is at least on more page, but we don't care */
4923 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4924 todo_wine {
4925 ok(r == len, "Expected %d, got %d\n", len, r);
4929 ReleaseDC(NULL, hdc);
4930 DestroyWindow(hwndRichEdit);
4933 static int nCallbackCount = 0;
4935 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4936 LONG cb, LONG* pcb)
4938 const char text[] = {'t','e','s','t'};
4940 if (sizeof(text) <= cb)
4942 if ((int)dwCookie != nCallbackCount)
4944 *pcb = 0;
4945 return 0;
4948 memcpy (pbBuff, text, sizeof(text));
4949 *pcb = sizeof(text);
4951 nCallbackCount++;
4953 return 0;
4955 else
4956 return 1; /* indicates callback failed */
4959 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4960 LPBYTE pbBuff,
4961 LONG cb,
4962 LONG *pcb)
4964 const char** str = (const char**)dwCookie;
4965 int size = strlen(*str);
4966 *pcb = cb;
4967 if (*pcb > size) {
4968 *pcb = size;
4970 if (*pcb > 0) {
4971 memcpy(pbBuff, *str, *pcb);
4972 *str += *pcb;
4974 return 0;
4977 struct StringWithLength {
4978 int length;
4979 char *buffer;
4982 /* This callback is used to handled the null characters in a string. */
4983 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4984 LPBYTE pbBuff,
4985 LONG cb,
4986 LONG *pcb)
4988 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4989 int size = str->length;
4990 *pcb = cb;
4991 if (*pcb > size) {
4992 *pcb = size;
4994 if (*pcb > 0) {
4995 memcpy(pbBuff, str->buffer, *pcb);
4996 str->buffer += *pcb;
4997 str->length -= *pcb;
4999 return 0;
5002 static void test_EM_STREAMIN(void)
5004 HWND hwndRichEdit = new_richedit(NULL);
5005 LRESULT result;
5006 EDITSTREAM es;
5007 char buffer[1024] = {0};
5009 const char * streamText0 = "{\\rtf1 TestSomeText}";
5010 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5011 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5013 const char * streamText1 =
5014 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5015 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5016 "}\r\n";
5018 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5019 const char * streamText2 =
5020 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5021 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5022 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5023 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5024 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5025 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5026 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5028 const char * streamText3 = "RichEdit1";
5030 struct StringWithLength cookieForStream4;
5031 const char * streamText4 =
5032 "This text just needs to be long enough to cause run to be split onto "
5033 "two separate lines and make sure the null terminating character is "
5034 "handled properly.\0";
5035 int length4 = strlen(streamText4) + 1;
5036 cookieForStream4.buffer = (char *)streamText4;
5037 cookieForStream4.length = length4;
5039 /* Minimal test without \par at the end */
5040 es.dwCookie = (DWORD_PTR)&streamText0;
5041 es.dwError = 0;
5042 es.pfnCallback = test_EM_STREAMIN_esCallback;
5043 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5045 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5046 ok (result == 12,
5047 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5048 result = strcmp (buffer,"TestSomeText");
5049 ok (result == 0,
5050 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5051 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5053 /* Native richedit 2.0 ignores last \par */
5054 es.dwCookie = (DWORD_PTR)&streamText0a;
5055 es.dwError = 0;
5056 es.pfnCallback = test_EM_STREAMIN_esCallback;
5057 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5059 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5060 ok (result == 12,
5061 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5062 result = strcmp (buffer,"TestSomeText");
5063 ok (result == 0,
5064 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5065 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5067 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5068 es.dwCookie = (DWORD_PTR)&streamText0b;
5069 es.dwError = 0;
5070 es.pfnCallback = test_EM_STREAMIN_esCallback;
5071 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5073 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5074 ok (result == 14,
5075 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5076 result = strcmp (buffer,"TestSomeText\r\n");
5077 ok (result == 0,
5078 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5079 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5081 es.dwCookie = (DWORD_PTR)&streamText1;
5082 es.dwError = 0;
5083 es.pfnCallback = test_EM_STREAMIN_esCallback;
5084 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5086 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5087 ok (result == 12,
5088 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5089 result = strcmp (buffer,"TestSomeText");
5090 ok (result == 0,
5091 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5092 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5094 es.dwCookie = (DWORD_PTR)&streamText2;
5095 es.dwError = 0;
5096 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5098 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5099 ok (result == 0,
5100 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5101 ok (strlen(buffer) == 0,
5102 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5103 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5105 es.dwCookie = (DWORD_PTR)&streamText3;
5106 es.dwError = 0;
5107 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5109 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5110 ok (result == 0,
5111 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5112 ok (strlen(buffer) == 0,
5113 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5114 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5116 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5117 es.dwError = 0;
5118 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5119 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5121 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5122 ok (result == length4,
5123 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5124 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5126 DestroyWindow(hwndRichEdit);
5129 static void test_EM_StreamIn_Undo(void)
5131 /* The purpose of this test is to determine when a EM_StreamIn should be
5132 * undoable. This is important because WM_PASTE currently uses StreamIn and
5133 * pasting should always be undoable but streaming isn't always.
5135 * cases to test:
5136 * StreamIn plain text without SFF_SELECTION.
5137 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5138 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5139 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5140 * Feel free to add tests for other text modes or StreamIn things.
5144 HWND hwndRichEdit = new_richedit(NULL);
5145 LRESULT result;
5146 EDITSTREAM es;
5147 char buffer[1024] = {0};
5148 const char randomtext[] = "Some text";
5150 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5152 /* StreamIn, no SFF_SELECTION */
5153 es.dwCookie = nCallbackCount;
5154 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5155 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5156 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5157 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5158 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5159 result = strcmp (buffer,"test");
5160 ok (result == 0,
5161 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5163 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5164 ok (result == FALSE,
5165 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5167 /* StreamIn, SFF_SELECTION, but nothing selected */
5168 es.dwCookie = nCallbackCount;
5169 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5170 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5171 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5172 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5173 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5174 result = strcmp (buffer,"testSome text");
5175 ok (result == 0,
5176 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5178 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5179 ok (result == TRUE,
5180 "EM_STREAMIN with SFF_SELECTION but no selection set "
5181 "should create an undo\n");
5183 /* StreamIn, SFF_SELECTION, with a selection */
5184 es.dwCookie = nCallbackCount;
5185 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5186 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5187 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5188 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5189 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5190 result = strcmp (buffer,"Sometesttext");
5191 ok (result == 0,
5192 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5194 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5195 ok (result == TRUE,
5196 "EM_STREAMIN with SFF_SELECTION and selection set "
5197 "should create an undo\n");
5199 DestroyWindow(hwndRichEdit);
5202 static BOOL is_em_settextex_supported(HWND hwnd)
5204 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5205 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5208 static void test_unicode_conversions(void)
5210 static const WCHAR tW[] = {'t',0};
5211 static const WCHAR teW[] = {'t','e',0};
5212 static const WCHAR textW[] = {'t','e','s','t',0};
5213 static const char textA[] = "test";
5214 char bufA[64];
5215 WCHAR bufW[64];
5216 HWND hwnd;
5217 int em_settextex_supported, ret;
5219 #define set_textA(hwnd, wm_set_text, txt) \
5220 do { \
5221 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5222 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5223 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5224 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5225 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5226 } while(0)
5227 #define expect_textA(hwnd, wm_get_text, txt) \
5228 do { \
5229 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5230 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5231 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5232 memset(bufA, 0xAA, sizeof(bufA)); \
5233 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5234 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5235 ret = lstrcmpA(bufA, txt); \
5236 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5237 } while(0)
5239 #define set_textW(hwnd, wm_set_text, txt) \
5240 do { \
5241 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5242 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5243 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5244 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5245 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5246 } while(0)
5247 #define expect_textW(hwnd, wm_get_text, txt) \
5248 do { \
5249 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5250 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5251 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5252 memset(bufW, 0xAA, sizeof(bufW)); \
5253 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5254 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 ok(ret, "RichEdit20W should be unicode under NT\n");
5276 /* EM_SETTEXTEX is supported starting from version 3.0 */
5277 em_settextex_supported = is_em_settextex_supported(hwnd);
5278 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5279 em_settextex_supported ? "" : "NOT ");
5281 expect_empty(hwnd, WM_GETTEXT);
5282 expect_empty(hwnd, EM_GETTEXTEX);
5284 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5285 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5286 expect_textA(hwnd, WM_GETTEXT, "t");
5287 expect_textA(hwnd, EM_GETTEXTEX, "t");
5288 expect_textW(hwnd, EM_GETTEXTEX, tW);
5290 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5291 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5292 expect_textA(hwnd, WM_GETTEXT, "te");
5293 expect_textA(hwnd, EM_GETTEXTEX, "te");
5294 expect_textW(hwnd, EM_GETTEXTEX, teW);
5296 set_textA(hwnd, WM_SETTEXT, NULL);
5297 expect_empty(hwnd, WM_GETTEXT);
5298 expect_empty(hwnd, EM_GETTEXTEX);
5300 set_textA(hwnd, WM_SETTEXT, textA);
5301 expect_textA(hwnd, WM_GETTEXT, textA);
5302 expect_textA(hwnd, EM_GETTEXTEX, textA);
5303 expect_textW(hwnd, EM_GETTEXTEX, textW);
5305 if (em_settextex_supported)
5307 set_textA(hwnd, EM_SETTEXTEX, textA);
5308 expect_textA(hwnd, WM_GETTEXT, textA);
5309 expect_textA(hwnd, EM_GETTEXTEX, textA);
5310 expect_textW(hwnd, EM_GETTEXTEX, textW);
5313 set_textW(hwnd, WM_SETTEXT, textW);
5314 expect_textW(hwnd, WM_GETTEXT, textW);
5315 expect_textA(hwnd, WM_GETTEXT, textA);
5316 expect_textW(hwnd, EM_GETTEXTEX, textW);
5317 expect_textA(hwnd, EM_GETTEXTEX, textA);
5319 if (em_settextex_supported)
5321 set_textW(hwnd, EM_SETTEXTEX, 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 DestroyWindow(hwnd);
5329 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5330 0, 0, 200, 60, 0, 0, 0, 0);
5331 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5333 ret = IsWindowUnicode(hwnd);
5334 ok(!ret, "RichEdit20A should NOT be unicode\n");
5336 set_textA(hwnd, WM_SETTEXT, textA);
5337 expect_textA(hwnd, WM_GETTEXT, textA);
5338 expect_textA(hwnd, EM_GETTEXTEX, textA);
5339 expect_textW(hwnd, EM_GETTEXTEX, textW);
5341 if (em_settextex_supported)
5343 set_textA(hwnd, EM_SETTEXTEX, textA);
5344 expect_textA(hwnd, WM_GETTEXT, textA);
5345 expect_textA(hwnd, EM_GETTEXTEX, textA);
5346 expect_textW(hwnd, EM_GETTEXTEX, textW);
5349 set_textW(hwnd, WM_SETTEXT, textW);
5350 expect_textW(hwnd, WM_GETTEXT, textW);
5351 expect_textA(hwnd, WM_GETTEXT, textA);
5352 expect_textW(hwnd, EM_GETTEXTEX, textW);
5353 expect_textA(hwnd, EM_GETTEXTEX, textA);
5355 if (em_settextex_supported)
5357 set_textW(hwnd, EM_SETTEXTEX, textW);
5358 expect_textW(hwnd, WM_GETTEXT, textW);
5359 expect_textA(hwnd, WM_GETTEXT, textA);
5360 expect_textW(hwnd, EM_GETTEXTEX, textW);
5361 expect_textA(hwnd, EM_GETTEXTEX, textA);
5363 DestroyWindow(hwnd);
5366 static void test_WM_CHAR(void)
5368 HWND hwnd;
5369 int ret;
5370 const char * char_list = "abc\rabc\r";
5371 const char * expected_content_single = "abcabc";
5372 const char * expected_content_multi = "abc\r\nabc\r\n";
5373 char buffer[64] = {0};
5374 const char * p;
5376 /* single-line control must IGNORE carriage returns */
5377 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5378 0, 0, 200, 60, 0, 0, 0, 0);
5379 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5381 p = char_list;
5382 while (*p != '\0') {
5383 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5384 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5385 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5386 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5387 p++;
5390 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5391 ret = strcmp(buffer, expected_content_single);
5392 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5394 DestroyWindow(hwnd);
5396 /* multi-line control inserts CR normally */
5397 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5398 0, 0, 200, 60, 0, 0, 0, 0);
5399 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5401 p = char_list;
5402 while (*p != '\0') {
5403 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5404 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5405 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5406 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5407 p++;
5410 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5411 ret = strcmp(buffer, expected_content_multi);
5412 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5414 DestroyWindow(hwnd);
5417 static void test_EM_GETTEXTLENGTHEX(void)
5419 HWND hwnd;
5420 GETTEXTLENGTHEX gtl;
5421 int ret;
5422 const char * base_string = "base string";
5423 const char * test_string = "a\nb\n\n\r\n";
5424 const char * test_string_after = "a";
5425 const char * test_string_2 = "a\rtest\rstring";
5426 char buffer[64] = {0};
5428 /* single line */
5429 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5430 0, 0, 200, 60, 0, 0, 0, 0);
5431 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5433 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5434 gtl.codepage = CP_ACP;
5435 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5436 ok(ret == 0, "ret %d\n",ret);
5438 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5439 gtl.codepage = CP_ACP;
5440 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5441 ok(ret == 0, "ret %d\n",ret);
5443 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5445 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5446 gtl.codepage = CP_ACP;
5447 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5448 ok(ret == strlen(base_string), "ret %d\n",ret);
5450 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5451 gtl.codepage = CP_ACP;
5452 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5453 ok(ret == strlen(base_string), "ret %d\n",ret);
5455 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5457 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5458 gtl.codepage = CP_ACP;
5459 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5460 ok(ret == 1, "ret %d\n",ret);
5462 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5463 gtl.codepage = CP_ACP;
5464 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5465 ok(ret == 1, "ret %d\n",ret);
5467 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5468 ret = strcmp(buffer, test_string_after);
5469 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5471 DestroyWindow(hwnd);
5473 /* multi line */
5474 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5475 0, 0, 200, 60, 0, 0, 0, 0);
5476 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5478 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5479 gtl.codepage = CP_ACP;
5480 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5481 ok(ret == 0, "ret %d\n",ret);
5483 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5484 gtl.codepage = CP_ACP;
5485 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5486 ok(ret == 0, "ret %d\n",ret);
5488 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5490 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5491 gtl.codepage = CP_ACP;
5492 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5493 ok(ret == strlen(base_string), "ret %d\n",ret);
5495 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5496 gtl.codepage = CP_ACP;
5497 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5498 ok(ret == strlen(base_string), "ret %d\n",ret);
5500 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5502 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5503 gtl.codepage = CP_ACP;
5504 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5505 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5507 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5508 gtl.codepage = CP_ACP;
5509 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5510 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5512 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5514 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5515 gtl.codepage = CP_ACP;
5516 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5517 ok(ret == 10, "ret %d\n",ret);
5519 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5520 gtl.codepage = CP_ACP;
5521 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5522 ok(ret == 6, "ret %d\n",ret);
5524 /* Unicode/NUMCHARS/NUMBYTES */
5525 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5527 gtl.flags = GTL_DEFAULT;
5528 gtl.codepage = 1200;
5529 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5530 ok(ret == lstrlen(test_string_2),
5531 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlen(test_string_2));
5533 gtl.flags = GTL_NUMCHARS;
5534 gtl.codepage = 1200;
5535 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5536 ok(ret == lstrlen(test_string_2),
5537 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlen(test_string_2));
5539 gtl.flags = GTL_NUMBYTES;
5540 gtl.codepage = 1200;
5541 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5542 ok(ret == lstrlen(test_string_2)*2,
5543 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5545 gtl.flags = GTL_PRECISE;
5546 gtl.codepage = 1200;
5547 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5548 ok(ret == lstrlen(test_string_2)*2,
5549 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5551 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5552 gtl.codepage = 1200;
5553 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5554 ok(ret == lstrlen(test_string_2),
5555 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2));
5557 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5558 gtl.codepage = 1200;
5559 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5560 ok(ret == E_INVALIDARG,
5561 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5563 DestroyWindow(hwnd);
5567 /* globals that parent and child access when checking event masks & notifications */
5568 static HWND eventMaskEditHwnd = 0;
5569 static int queriedEventMask;
5570 static int watchForEventMask = 0;
5572 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5573 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5575 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5577 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5579 return DefWindowProcA(hwnd, message, wParam, lParam);
5582 /* test event masks in combination with WM_COMMAND */
5583 static void test_eventMask(void)
5585 HWND parent;
5586 int ret, style;
5587 WNDCLASSA cls;
5588 const char text[] = "foo bar\n";
5589 int eventMask;
5591 /* register class to capture WM_COMMAND */
5592 cls.style = 0;
5593 cls.lpfnWndProc = ParentMsgCheckProcA;
5594 cls.cbClsExtra = 0;
5595 cls.cbWndExtra = 0;
5596 cls.hInstance = GetModuleHandleA(0);
5597 cls.hIcon = 0;
5598 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5599 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5600 cls.lpszMenuName = NULL;
5601 cls.lpszClassName = "EventMaskParentClass";
5602 if(!RegisterClassA(&cls)) assert(0);
5604 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5605 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5606 ok (parent != 0, "Failed to create parent window\n");
5608 eventMaskEditHwnd = new_richedit(parent);
5609 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5611 eventMask = ENM_CHANGE | ENM_UPDATE;
5612 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5613 ok(ret == ENM_NONE, "wrong event mask\n");
5614 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5615 ok(ret == eventMask, "failed to set event mask\n");
5617 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5618 queriedEventMask = 0; /* initialize to something other than we expect */
5619 watchForEventMask = EN_CHANGE;
5620 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5621 ok(ret == TRUE, "failed to set text\n");
5622 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5623 notification in response to WM_SETTEXT */
5624 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5625 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5627 /* check to see if EN_CHANGE is sent when redraw is turned off */
5628 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5629 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5630 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5631 /* redraw is disabled by making the window invisible. */
5632 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't 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 & ~ENM_CHANGE),
5636 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5637 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5638 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5640 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5641 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5642 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5643 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5644 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5645 watchForEventMask = EN_UPDATE;
5646 queriedEventMask = 0; /* initialize to something other than we expect */
5647 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5648 ok(queriedEventMask == 0,
5649 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5650 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5651 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5652 queriedEventMask = 0; /* initialize to something other than we expect */
5653 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5654 ok(queriedEventMask == eventMask,
5655 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5658 DestroyWindow(parent);
5661 static int received_WM_NOTIFY = 0;
5662 static int modify_at_WM_NOTIFY = 0;
5663 static BOOL filter_on_WM_NOTIFY = FALSE;
5664 static HWND hwndRichedit_WM_NOTIFY;
5666 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5668 if(message == WM_NOTIFY)
5670 received_WM_NOTIFY = 1;
5671 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5672 if (filter_on_WM_NOTIFY) return TRUE;
5674 return DefWindowProcA(hwnd, message, wParam, lParam);
5677 static void test_WM_NOTIFY(void)
5679 HWND parent;
5680 WNDCLASSA cls;
5681 CHARFORMAT2 cf2;
5682 int sel_start, sel_end;
5684 /* register class to capture WM_NOTIFY */
5685 cls.style = 0;
5686 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5687 cls.cbClsExtra = 0;
5688 cls.cbWndExtra = 0;
5689 cls.hInstance = GetModuleHandleA(0);
5690 cls.hIcon = 0;
5691 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5692 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5693 cls.lpszMenuName = NULL;
5694 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5695 if(!RegisterClassA(&cls)) assert(0);
5697 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5698 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5699 ok (parent != 0, "Failed to create parent window\n");
5701 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5702 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5704 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5706 /* Notifications for selection change should only be sent when selection
5707 actually changes. EM_SETCHARFORMAT is one message that calls
5708 ME_CommitUndo, which should check whether message should be sent */
5709 received_WM_NOTIFY = 0;
5710 cf2.cbSize = sizeof(CHARFORMAT2);
5711 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5712 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5713 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5714 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5715 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5717 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5718 already at 0. */
5719 received_WM_NOTIFY = 0;
5720 modify_at_WM_NOTIFY = 0;
5721 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5722 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5723 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5725 received_WM_NOTIFY = 0;
5726 modify_at_WM_NOTIFY = 0;
5727 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5728 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5730 received_WM_NOTIFY = 0;
5731 modify_at_WM_NOTIFY = 0;
5732 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5733 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5734 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5736 /* Test for WM_NOTIFY messages with redraw disabled. */
5737 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5738 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5739 received_WM_NOTIFY = 0;
5740 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5741 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5742 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5744 /* Test filtering key events. */
5745 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5746 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5747 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5748 received_WM_NOTIFY = 0;
5749 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5750 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5751 ok(sel_start == 1 && sel_end == 1,
5752 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5753 filter_on_WM_NOTIFY = TRUE;
5754 received_WM_NOTIFY = 0;
5755 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5756 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5757 ok(sel_start == 1 && sel_end == 1,
5758 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5760 /* test with owner set to NULL */
5761 SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5762 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5763 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5764 ok(sel_start == 1 && sel_end == 1,
5765 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5767 DestroyWindow(hwndRichedit_WM_NOTIFY);
5768 DestroyWindow(parent);
5771 static void test_undo_coalescing(void)
5773 HWND hwnd;
5774 int result;
5775 char buffer[64] = {0};
5777 /* multi-line control inserts CR normally */
5778 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5779 0, 0, 200, 60, 0, 0, 0, 0);
5780 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5782 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5783 ok (result == FALSE, "Can undo after window creation.\n");
5784 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5785 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5786 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5787 ok (result == FALSE, "Can redo after window creation.\n");
5788 result = SendMessage(hwnd, EM_REDO, 0, 0);
5789 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5791 /* Test the effect of arrows keys during typing on undo transactions*/
5792 simulate_typing_characters(hwnd, "one two three");
5793 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5794 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5795 simulate_typing_characters(hwnd, " four five six");
5797 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5798 ok (result == FALSE, "Can redo before anything is undone.\n");
5799 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5800 ok (result == TRUE, "Cannot undo typed characters.\n");
5801 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5802 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5803 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5804 ok (result == TRUE, "Cannot redo after undo.\n");
5805 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5806 result = strcmp(buffer, "one two three");
5807 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5809 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5810 ok (result == TRUE, "Cannot undo typed characters.\n");
5811 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5812 ok (result == TRUE, "Failed to undo typed characters.\n");
5813 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5814 result = strcmp(buffer, "");
5815 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5817 /* Test the effect of focus changes during typing on undo transactions*/
5818 simulate_typing_characters(hwnd, "one two three");
5819 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5820 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5821 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5822 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5823 simulate_typing_characters(hwnd, " four five six");
5824 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5825 ok (result == TRUE, "Failed to undo typed characters.\n");
5826 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5827 result = strcmp(buffer, "one two three");
5828 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5830 /* Test the effect of the back key during typing on undo transactions */
5831 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5832 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5833 ok (result == TRUE, "Failed to clear the text.\n");
5834 simulate_typing_characters(hwnd, "one two threa");
5835 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5836 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5837 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5838 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5839 simulate_typing_characters(hwnd, "e four five six");
5840 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5841 ok (result == TRUE, "Failed to undo typed characters.\n");
5842 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5843 result = strcmp(buffer, "");
5844 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5846 /* Test the effect of the delete key during typing on undo transactions */
5847 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5848 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5849 ok(result == TRUE, "Failed to set the text.\n");
5850 SendMessage(hwnd, EM_SETSEL, 1, 1);
5851 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5852 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5853 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5854 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5855 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5856 ok (result == TRUE, "Failed to undo typed characters.\n");
5857 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5858 result = strcmp(buffer, "acd");
5859 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
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, "abcd");
5864 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5866 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5867 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5868 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5869 ok (result == TRUE, "Failed to clear the text.\n");
5870 simulate_typing_characters(hwnd, "one two three");
5871 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5872 ok (result == 0, "expected %d but got %d\n", 0, result);
5873 simulate_typing_characters(hwnd, " four five six");
5874 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5875 ok (result == TRUE, "Failed to undo typed characters.\n");
5876 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5877 result = strcmp(buffer, "one two three");
5878 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5879 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5880 ok (result == TRUE, "Failed to undo typed characters.\n");
5881 ok (result == TRUE, "Failed to undo typed characters.\n");
5882 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5883 result = strcmp(buffer, "");
5884 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5886 DestroyWindow(hwnd);
5889 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5891 int length;
5893 /* MSDN lied, length is actually the number of bytes. */
5894 length = bytes / sizeof(WCHAR);
5895 switch(code)
5897 case WB_ISDELIMITER:
5898 return text[pos] == 'X';
5899 case WB_LEFT:
5900 case WB_MOVEWORDLEFT:
5901 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5902 return pos-1;
5903 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5904 case WB_LEFTBREAK:
5905 pos--;
5906 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5907 pos--;
5908 return pos;
5909 case WB_RIGHT:
5910 case WB_MOVEWORDRIGHT:
5911 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5912 return pos+1;
5913 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5914 case WB_RIGHTBREAK:
5915 pos++;
5916 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5917 pos++;
5918 return pos;
5919 default:
5920 ok(FALSE, "Unexpected code %d\n", code);
5921 break;
5923 return 0;
5926 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5927 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5929 static void test_word_movement(void)
5931 HWND hwnd;
5932 int result;
5933 int sel_start, sel_end;
5934 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5936 /* multi-line control inserts CR normally */
5937 hwnd = new_richedit(NULL);
5939 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5940 ok (result == TRUE, "Failed to clear the text.\n");
5941 SendMessage(hwnd, EM_SETSEL, 0, 0);
5942 /* |one two three */
5944 SEND_CTRL_RIGHT(hwnd);
5945 /* one |two three */
5946 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5947 ok(sel_start == sel_end, "Selection should be empty\n");
5948 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5950 SEND_CTRL_RIGHT(hwnd);
5951 /* one two |three */
5952 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5953 ok(sel_start == sel_end, "Selection should be empty\n");
5954 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5956 SEND_CTRL_LEFT(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 == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5962 SEND_CTRL_LEFT(hwnd);
5963 /* |one two three */
5964 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5965 ok(sel_start == sel_end, "Selection should be empty\n");
5966 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5968 SendMessage(hwnd, EM_SETSEL, 8, 8);
5969 /* one two | three */
5970 SEND_CTRL_RIGHT(hwnd);
5971 /* one two |three */
5972 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5973 ok(sel_start == sel_end, "Selection should be empty\n");
5974 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5976 SendMessage(hwnd, EM_SETSEL, 11, 11);
5977 /* one two th|ree */
5978 SEND_CTRL_LEFT(hwnd);
5979 /* one two |three */
5980 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5981 ok(sel_start == sel_end, "Selection should be empty\n");
5982 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5984 /* Test with a custom word break procedure that uses X as the delimiter. */
5985 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5986 ok (result == TRUE, "Failed to clear the text.\n");
5987 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5988 /* |one twoXthree */
5989 SEND_CTRL_RIGHT(hwnd);
5990 /* one twoX|three */
5991 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5992 ok(sel_start == sel_end, "Selection should be empty\n");
5993 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5995 DestroyWindow(hwnd);
5997 /* Make sure the behaviour is the same with a unicode richedit window,
5998 * and using unicode functions. */
6000 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6001 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6002 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6004 /* Test with a custom word break procedure that uses X as the delimiter. */
6005 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6006 ok (result == TRUE, "Failed to clear the text.\n");
6007 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6008 /* |one twoXthree */
6009 SEND_CTRL_RIGHT(hwnd);
6010 /* one twoX|three */
6011 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6012 ok(sel_start == sel_end, "Selection should be empty\n");
6013 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6015 DestroyWindow(hwnd);
6018 static void test_EM_CHARFROMPOS(void)
6020 HWND hwnd;
6021 int result;
6022 RECT rcClient;
6023 POINTL point;
6024 point.x = 0;
6025 point.y = 40;
6027 /* multi-line control inserts CR normally */
6028 hwnd = new_richedit(NULL);
6029 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6030 (LPARAM)"one two three four five six seven\reight");
6031 ok(result == 1, "Expected 1, got %d\n", result);
6032 GetClientRect(hwnd, &rcClient);
6034 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6035 ok(result == 34, "expected character index of 34 but got %d\n", result);
6037 /* Test with points outside the bounds of the richedit control. */
6038 point.x = -1;
6039 point.y = 40;
6040 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6041 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6043 point.x = 1000;
6044 point.y = 0;
6045 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6046 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6048 point.x = 1000;
6049 point.y = 36;
6050 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6051 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6053 point.x = 1000;
6054 point.y = -1;
6055 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6056 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6058 point.x = 1000;
6059 point.y = rcClient.bottom + 1;
6060 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6061 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6063 point.x = 1000;
6064 point.y = rcClient.bottom;
6065 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6066 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6068 DestroyWindow(hwnd);
6071 static void test_word_wrap(void)
6073 HWND hwnd;
6074 POINTL point = {0, 60}; /* This point must be below the first line */
6075 const char *text = "Must be long enough to test line wrapping";
6076 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6077 int res, pos, lines;
6079 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6080 * when specified on window creation and set later. */
6081 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6082 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6083 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6084 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6085 ok(res, "WM_SETTEXT failed.\n");
6086 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6087 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6088 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6089 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6091 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6092 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6093 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6094 DestroyWindow(hwnd);
6096 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6097 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6098 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6100 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6101 ok(res, "WM_SETTEXT failed.\n");
6102 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6103 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6104 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6105 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6107 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6108 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6109 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6110 DestroyWindow(hwnd);
6112 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6113 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6114 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6115 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6116 ok(res, "WM_SETTEXT failed.\n");
6117 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6118 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6120 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6121 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6122 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6123 DestroyWindow(hwnd);
6125 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6126 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6127 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6128 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6129 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6130 ok(res, "WM_SETTEXT failed.\n");
6131 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6132 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6134 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6135 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6136 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6138 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6139 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6140 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6141 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6142 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6144 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6145 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6146 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6147 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6148 DestroyWindow(hwnd);
6150 /* Test to see if wrapping happens with redraw disabled. */
6151 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6152 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6153 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6154 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6155 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6156 ok(res, "EM_REPLACESEL failed.\n");
6157 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6158 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6159 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6160 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6161 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6163 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6164 DestroyWindow(hwnd);
6167 static void test_autoscroll(void)
6169 HWND hwnd = new_richedit(NULL);
6170 int lines, ret, redraw;
6171 POINT pt;
6173 for (redraw = 0; redraw <= 1; redraw++) {
6174 trace("testing with WM_SETREDRAW=%d\n", redraw);
6175 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6176 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6177 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6178 ok(lines == 8, "%d lines instead of 8\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, "Didn't scroll down after replacing text.\n");
6182 ret = GetWindowLong(hwnd, GWL_STYLE);
6183 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6185 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6186 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6187 ok(lines == 1, "%d lines instead of 1\n", lines);
6188 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6189 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6190 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6191 ret = GetWindowLong(hwnd, GWL_STYLE);
6192 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6195 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6196 DestroyWindow(hwnd);
6198 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6199 * auto vertical/horizontal scrolling options. */
6200 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6201 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6202 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6203 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6204 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6205 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6206 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6207 ret = GetWindowLong(hwnd, GWL_STYLE);
6208 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6209 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6210 DestroyWindow(hwnd);
6212 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6213 WS_POPUP|ES_MULTILINE,
6214 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6215 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6216 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6217 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6218 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6219 ret = GetWindowLong(hwnd, GWL_STYLE);
6220 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6221 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6222 DestroyWindow(hwnd);
6226 static void test_format_rect(void)
6228 HWND hwnd;
6229 RECT rc, expected, clientRect;
6230 int n;
6231 DWORD options;
6233 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6234 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6235 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6236 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6238 GetClientRect(hwnd, &clientRect);
6240 expected = clientRect;
6241 expected.left += 1;
6242 expected.right -= 1;
6243 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6244 ok(rc.top == expected.top && rc.left == expected.left &&
6245 rc.bottom == expected.bottom && rc.right == expected.right,
6246 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6247 rc.top, rc.left, rc.bottom, rc.right,
6248 expected.top, expected.left, expected.bottom, expected.right);
6250 for (n = -3; n <= 3; n++)
6252 rc = clientRect;
6253 rc.top += n;
6254 rc.left += n;
6255 rc.bottom -= n;
6256 rc.right -= n;
6257 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6259 expected = rc;
6260 expected.top = max(0, rc.top);
6261 expected.left = max(0, rc.left);
6262 expected.bottom = min(clientRect.bottom, rc.bottom);
6263 expected.right = min(clientRect.right, rc.right);
6264 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6265 ok(rc.top == expected.top && rc.left == expected.left &&
6266 rc.bottom == expected.bottom && rc.right == expected.right,
6267 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6268 n, rc.top, rc.left, rc.bottom, rc.right,
6269 expected.top, expected.left, expected.bottom, expected.right);
6272 rc = clientRect;
6273 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6274 expected = clientRect;
6275 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6276 ok(rc.top == expected.top && rc.left == expected.left &&
6277 rc.bottom == expected.bottom && rc.right == expected.right,
6278 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6279 rc.top, rc.left, rc.bottom, rc.right,
6280 expected.top, expected.left, expected.bottom, expected.right);
6282 /* Adding the selectionbar adds the selectionbar width to the left side. */
6283 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6284 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6285 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6286 expected.left += 8; /* selection bar width */
6287 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6288 ok(rc.top == expected.top && rc.left == expected.left &&
6289 rc.bottom == expected.bottom && rc.right == expected.right,
6290 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6291 rc.top, rc.left, rc.bottom, rc.right,
6292 expected.top, expected.left, expected.bottom, expected.right);
6294 rc = clientRect;
6295 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6296 expected = clientRect;
6297 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6298 ok(rc.top == expected.top && rc.left == expected.left &&
6299 rc.bottom == expected.bottom && rc.right == expected.right,
6300 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6301 rc.top, rc.left, rc.bottom, rc.right,
6302 expected.top, expected.left, expected.bottom, expected.right);
6304 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6305 * even if the left side is already 0. */
6306 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6307 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6308 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6309 expected.left -= 8; /* selection bar width */
6310 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6311 ok(rc.top == expected.top && rc.left == expected.left &&
6312 rc.bottom == expected.bottom && rc.right == expected.right,
6313 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6314 rc.top, rc.left, rc.bottom, rc.right,
6315 expected.top, expected.left, expected.bottom, expected.right);
6317 /* Set the absolute value of the formatting rectangle. */
6318 rc = clientRect;
6319 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6320 expected = clientRect;
6321 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6322 ok(rc.top == expected.top && rc.left == expected.left &&
6323 rc.bottom == expected.bottom && rc.right == expected.right,
6324 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6325 n, rc.top, rc.left, rc.bottom, rc.right,
6326 expected.top, expected.left, expected.bottom, expected.right);
6328 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6329 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6330 * tests show that this isn't true. */
6331 rc.top = 15;
6332 rc.left = 15;
6333 rc.bottom = clientRect.bottom - 15;
6334 rc.right = clientRect.right - 15;
6335 expected = rc;
6336 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6337 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6338 ok(rc.top == expected.top && rc.left == expected.left &&
6339 rc.bottom == expected.bottom && rc.right == expected.right,
6340 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6341 rc.top, rc.left, rc.bottom, rc.right,
6342 expected.top, expected.left, expected.bottom, expected.right);
6344 /* For some reason it does not limit the values to the client rect with
6345 * a WPARAM value of 1. */
6346 rc.top = -15;
6347 rc.left = -15;
6348 rc.bottom = clientRect.bottom + 15;
6349 rc.right = clientRect.right + 15;
6350 expected = rc;
6351 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6352 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6353 ok(rc.top == expected.top && rc.left == expected.left &&
6354 rc.bottom == expected.bottom && rc.right == expected.right,
6355 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6356 rc.top, rc.left, rc.bottom, rc.right,
6357 expected.top, expected.left, expected.bottom, expected.right);
6359 DestroyWindow(hwnd);
6361 /* The extended window style affects the formatting rectangle. */
6362 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6363 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6364 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6365 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6367 GetClientRect(hwnd, &clientRect);
6369 expected = clientRect;
6370 expected.left += 1;
6371 expected.top += 1;
6372 expected.right -= 1;
6373 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6374 ok(rc.top == expected.top && rc.left == expected.left &&
6375 rc.bottom == expected.bottom && rc.right == expected.right,
6376 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6377 rc.top, rc.left, rc.bottom, rc.right,
6378 expected.top, expected.left, expected.bottom, expected.right);
6380 rc = clientRect;
6381 rc.top += 5;
6382 rc.left += 5;
6383 rc.bottom -= 5;
6384 rc.right -= 5;
6385 expected = rc;
6386 expected.top -= 1;
6387 expected.left -= 1;
6388 expected.right += 1;
6389 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6390 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6391 ok(rc.top == expected.top && rc.left == expected.left &&
6392 rc.bottom == expected.bottom && rc.right == expected.right,
6393 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6394 rc.top, rc.left, rc.bottom, rc.right,
6395 expected.top, expected.left, expected.bottom, expected.right);
6397 DestroyWindow(hwnd);
6400 static void test_WM_GETDLGCODE(void)
6402 HWND hwnd;
6403 UINT res, expected;
6404 MSG msg;
6406 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6408 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6409 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6410 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6411 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6412 msg.hwnd = hwnd;
6413 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6414 expected = expected | DLGC_WANTMESSAGE;
6415 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6416 res, expected);
6417 DestroyWindow(hwnd);
6419 msg.message = WM_KEYDOWN;
6420 msg.wParam = VK_RETURN;
6421 msg.lParam = (MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6422 msg.pt.x = 0;
6423 msg.pt.y = 0;
6424 msg.time = GetTickCount();
6426 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6427 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6428 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6429 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6430 msg.hwnd = hwnd;
6431 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6432 expected = expected | DLGC_WANTMESSAGE;
6433 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6434 res, expected);
6435 DestroyWindow(hwnd);
6437 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6438 ES_MULTILINE|WS_POPUP,
6439 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6440 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6441 msg.hwnd = hwnd;
6442 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6443 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6444 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6445 res, expected);
6446 DestroyWindow(hwnd);
6448 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6449 ES_WANTRETURN|WS_POPUP,
6450 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6451 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6452 msg.hwnd = hwnd;
6453 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6454 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6455 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6456 res, expected);
6457 DestroyWindow(hwnd);
6459 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6460 WS_POPUP,
6461 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6462 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6463 msg.hwnd = hwnd;
6464 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6465 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6466 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6467 res, expected);
6468 DestroyWindow(hwnd);
6470 msg.wParam = VK_TAB;
6471 msg.lParam = (MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6473 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6474 ES_MULTILINE|WS_POPUP,
6475 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6476 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6477 msg.hwnd = hwnd;
6478 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6479 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6480 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6481 res, expected);
6482 DestroyWindow(hwnd);
6484 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6485 WS_POPUP,
6486 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6487 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6488 msg.hwnd = hwnd;
6489 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6490 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6491 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6492 res, expected);
6493 DestroyWindow(hwnd);
6495 hold_key(VK_CONTROL);
6497 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6498 ES_MULTILINE|WS_POPUP,
6499 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6500 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6501 msg.hwnd = hwnd;
6502 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6503 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6504 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6505 res, expected);
6506 DestroyWindow(hwnd);
6508 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6509 WS_POPUP,
6510 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6511 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6512 msg.hwnd = hwnd;
6513 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6514 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6515 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6516 res, expected);
6517 DestroyWindow(hwnd);
6519 release_key(VK_CONTROL);
6521 msg.wParam = 'a';
6522 msg.lParam = (MapVirtualKey('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6524 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6525 ES_MULTILINE|WS_POPUP,
6526 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6527 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6528 msg.hwnd = hwnd;
6529 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6530 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6531 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6532 res, expected);
6533 DestroyWindow(hwnd);
6535 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6536 WS_POPUP,
6537 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6538 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6539 msg.hwnd = hwnd;
6540 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6541 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6542 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6543 res, expected);
6544 DestroyWindow(hwnd);
6546 msg.message = WM_CHAR;
6548 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6549 ES_MULTILINE|WS_POPUP,
6550 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6551 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6552 msg.hwnd = hwnd;
6553 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6554 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6555 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6556 res, expected);
6557 DestroyWindow(hwnd);
6559 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6560 WS_POPUP,
6561 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6562 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6563 msg.hwnd = hwnd;
6564 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6565 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6566 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6567 res, expected);
6568 DestroyWindow(hwnd);
6571 static void test_zoom(void)
6573 HWND hwnd;
6574 UINT ret;
6575 RECT rc;
6576 POINT pt;
6577 int numerator, denominator;
6579 hwnd = new_richedit(NULL);
6580 GetClientRect(hwnd, &rc);
6581 pt.x = (rc.right - rc.left) / 2;
6582 pt.y = (rc.bottom - rc.top) / 2;
6583 ClientToScreen(hwnd, &pt);
6585 /* Test initial zoom value */
6586 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6587 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6588 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6589 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6591 /* test scroll wheel */
6592 hold_key(VK_CONTROL);
6593 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6594 MAKELPARAM(pt.x, pt.y));
6595 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6596 release_key(VK_CONTROL);
6598 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6599 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6600 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6601 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6603 /* Test how much the mouse wheel can zoom in and out. */
6604 ret = SendMessage(hwnd, EM_SETZOOM, 490, 100);
6605 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6607 hold_key(VK_CONTROL);
6608 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6609 MAKELPARAM(pt.x, pt.y));
6610 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6611 release_key(VK_CONTROL);
6613 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6614 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6615 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6616 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6618 ret = SendMessage(hwnd, EM_SETZOOM, 491, 100);
6619 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6621 hold_key(VK_CONTROL);
6622 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6623 MAKELPARAM(pt.x, pt.y));
6624 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6625 release_key(VK_CONTROL);
6627 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6628 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6629 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6630 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6632 ret = SendMessage(hwnd, EM_SETZOOM, 20, 100);
6633 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6635 hold_key(VK_CONTROL);
6636 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6637 MAKELPARAM(pt.x, pt.y));
6638 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6639 release_key(VK_CONTROL);
6641 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6642 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6643 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6644 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6646 ret = SendMessage(hwnd, EM_SETZOOM, 19, 100);
6647 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6649 hold_key(VK_CONTROL);
6650 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6651 MAKELPARAM(pt.x, pt.y));
6652 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6653 release_key(VK_CONTROL);
6655 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6656 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6657 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6658 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6660 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6661 ret = SendMessage(hwnd, EM_SETZOOM, 50, 13);
6662 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6664 hold_key(VK_CONTROL);
6665 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6666 MAKELPARAM(pt.x, pt.y));
6667 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6668 release_key(VK_CONTROL);
6670 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6671 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6672 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6673 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6675 /* Test bounds checking on EM_SETZOOM */
6676 ret = SendMessage(hwnd, EM_SETZOOM, 2, 127);
6677 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6679 ret = SendMessage(hwnd, EM_SETZOOM, 127, 2);
6680 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6682 ret = SendMessage(hwnd, EM_SETZOOM, 2, 128);
6683 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6685 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6686 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6687 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6688 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6690 ret = SendMessage(hwnd, EM_SETZOOM, 128, 2);
6691 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6693 /* See if negative numbers are accepted. */
6694 ret = SendMessage(hwnd, EM_SETZOOM, -100, -100);
6695 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6697 /* See if negative numbers are accepted. */
6698 ret = SendMessage(hwnd, EM_SETZOOM, 0, 100);
6699 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6701 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6702 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6703 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6704 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6706 /* Reset the zoom value */
6707 ret = SendMessage(hwnd, EM_SETZOOM, 0, 0);
6708 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6710 DestroyWindow(hwnd);
6713 struct dialog_mode_messages
6715 int wm_getdefid, wm_close, wm_nextdlgctl;
6718 static struct dialog_mode_messages dm_messages;
6720 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6721 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6722 "got %d\n", wmclose, dm_messages.wm_close); \
6723 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6724 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6725 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6726 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6728 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6730 switch (iMsg)
6732 case DM_GETDEFID:
6733 dm_messages.wm_getdefid++;
6734 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6735 case WM_NEXTDLGCTL:
6736 dm_messages.wm_nextdlgctl++;
6737 break;
6738 case WM_CLOSE:
6739 dm_messages.wm_close++;
6740 break;
6743 return DefWindowProc(hwnd, iMsg, wParam, lParam);
6746 static void test_dialogmode(void)
6748 HWND hwRichEdit, hwParent, hwButton;
6749 MSG msg= {0};
6750 int lcount, r;
6751 WNDCLASSA cls;
6753 cls.style = 0;
6754 cls.lpfnWndProc = dialog_mode_wnd_proc;
6755 cls.cbClsExtra = 0;
6756 cls.cbWndExtra = 0;
6757 cls.hInstance = GetModuleHandleA(0);
6758 cls.hIcon = 0;
6759 cls.hCursor = LoadCursorA(0, IDC_ARROW);
6760 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6761 cls.lpszMenuName = NULL;
6762 cls.lpszClassName = "DialogModeParentClass";
6763 if(!RegisterClassA(&cls)) assert(0);
6765 hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6766 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6768 /* Test richedit(ES_MULTILINE) */
6770 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6772 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6773 ok(0 == r, "expected 0, got %d\n", r);
6774 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6775 ok(2 == lcount, "expected 2, got %d\n", lcount);
6777 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6778 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6780 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6781 ok(0 == r, "expected 0, got %d\n", r);
6782 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6783 ok(3 == lcount, "expected 3, got %d\n", lcount);
6785 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6786 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6787 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6788 ok(0 == r, "expected 0, got %d\n", r);
6789 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6790 ok(3 == lcount, "expected 3, got %d\n", lcount);
6792 DestroyWindow(hwRichEdit);
6794 /* Test standalone richedit(ES_MULTILINE) */
6796 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6798 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6799 ok(0 == r, "expected 0, got %d\n", r);
6800 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6801 ok(2 == lcount, "expected 2, got %d\n", lcount);
6803 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6804 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6806 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6807 ok(0 == r, "expected 0, got %d\n", r);
6808 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6809 ok(2 == lcount, "expected 2, got %d\n", lcount);
6811 DestroyWindow(hwRichEdit);
6813 /* Check a destination for messages */
6815 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6817 SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6818 SetParent( hwRichEdit, NULL);
6820 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6821 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6823 memset(&dm_messages, 0, sizeof(dm_messages));
6824 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6825 ok(0 == r, "expected 0, got %d\n", r);
6826 test_dm_messages(0, 1, 0);
6828 memset(&dm_messages, 0, sizeof(dm_messages));
6829 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6830 ok(0 == r, "expected 0, got %d\n", r);
6831 test_dm_messages(0, 0, 1);
6833 DestroyWindow(hwRichEdit);
6835 /* Check messages from richedit(ES_MULTILINE) */
6837 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6839 memset(&dm_messages, 0, sizeof(dm_messages));
6840 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6841 ok(0 == r, "expected 0, got %d\n", r);
6842 test_dm_messages(0, 0, 0);
6844 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6845 ok(2 == lcount, "expected 2, got %d\n", lcount);
6847 memset(&dm_messages, 0, sizeof(dm_messages));
6848 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6849 ok(0 == r, "expected 0, got %d\n", r);
6850 test_dm_messages(0, 0, 0);
6852 memset(&dm_messages, 0, sizeof(dm_messages));
6853 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6854 ok(0 == r, "expected 0, got %d\n", r);
6855 test_dm_messages(0, 0, 0);
6857 memset(&dm_messages, 0, sizeof(dm_messages));
6858 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6859 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6860 test_dm_messages(0, 0, 0);
6862 memset(&dm_messages, 0, sizeof(dm_messages));
6863 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6864 ok(0 == r, "expected 0, got %d\n", r);
6865 test_dm_messages(0, 1, 0);
6867 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6868 ok(2 == lcount, "expected 2, got %d\n", lcount);
6870 memset(&dm_messages, 0, sizeof(dm_messages));
6871 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6872 ok(0 == r, "expected 0, got %d\n", r);
6873 test_dm_messages(0, 0, 0);
6875 memset(&dm_messages, 0, sizeof(dm_messages));
6876 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6877 ok(0 == r, "expected 0, got %d\n", r);
6878 test_dm_messages(0, 0, 1);
6880 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6881 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6882 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6884 memset(&dm_messages, 0, sizeof(dm_messages));
6885 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6886 ok(0 == r, "expected 0, got %d\n", r);
6887 test_dm_messages(0, 1, 1);
6889 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6890 ok(2 == lcount, "expected 2, got %d\n", lcount);
6892 DestroyWindow(hwButton);
6893 DestroyWindow(hwRichEdit);
6895 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
6897 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, hwParent);
6899 memset(&dm_messages, 0, sizeof(dm_messages));
6900 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6901 ok(0 == r, "expected 0, got %d\n", r);
6902 test_dm_messages(0, 0, 0);
6904 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6905 ok(2 == lcount, "expected 2, got %d\n", lcount);
6907 memset(&dm_messages, 0, sizeof(dm_messages));
6908 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6909 ok(0 == r, "expected 0, got %d\n", r);
6910 test_dm_messages(0, 0, 0);
6912 memset(&dm_messages, 0, sizeof(dm_messages));
6913 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6914 ok(0 == r, "expected 0, got %d\n", r);
6915 test_dm_messages(0, 0, 0);
6917 memset(&dm_messages, 0, sizeof(dm_messages));
6918 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6919 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6920 test_dm_messages(0, 0, 0);
6922 memset(&dm_messages, 0, sizeof(dm_messages));
6923 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6924 ok(0 == r, "expected 0, got %d\n", r);
6925 test_dm_messages(0, 0, 0);
6927 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6928 ok(3 == lcount, "expected 3, got %d\n", lcount);
6930 memset(&dm_messages, 0, sizeof(dm_messages));
6931 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6932 ok(0 == r, "expected 0, got %d\n", r);
6933 test_dm_messages(0, 0, 0);
6935 memset(&dm_messages, 0, sizeof(dm_messages));
6936 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6937 ok(0 == r, "expected 0, got %d\n", r);
6938 test_dm_messages(0, 0, 1);
6940 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6941 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6942 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6944 memset(&dm_messages, 0, sizeof(dm_messages));
6945 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6946 ok(0 == r, "expected 0, got %d\n", r);
6947 test_dm_messages(0, 0, 0);
6949 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6950 ok(4 == lcount, "expected 4, got %d\n", lcount);
6952 DestroyWindow(hwButton);
6953 DestroyWindow(hwRichEdit);
6955 /* Check messages from richedit(0) */
6957 hwRichEdit = new_window(RICHEDIT_CLASS, 0, hwParent);
6959 memset(&dm_messages, 0, sizeof(dm_messages));
6960 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6961 ok(0 == r, "expected 0, got %d\n", r);
6962 test_dm_messages(0, 0, 0);
6964 memset(&dm_messages, 0, sizeof(dm_messages));
6965 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6966 ok(0 == r, "expected 0, got %d\n", r);
6967 test_dm_messages(0, 0, 0);
6969 memset(&dm_messages, 0, sizeof(dm_messages));
6970 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6971 ok(0 == r, "expected 0, got %d\n", r);
6972 test_dm_messages(0, 0, 0);
6974 memset(&dm_messages, 0, sizeof(dm_messages));
6975 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6976 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
6977 test_dm_messages(0, 0, 0);
6979 memset(&dm_messages, 0, sizeof(dm_messages));
6980 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6981 ok(0 == r, "expected 0, got %d\n", r);
6982 test_dm_messages(0, 1, 0);
6984 memset(&dm_messages, 0, sizeof(dm_messages));
6985 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6986 ok(0 == r, "expected 0, got %d\n", r);
6987 test_dm_messages(0, 0, 0);
6989 memset(&dm_messages, 0, sizeof(dm_messages));
6990 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6991 ok(0 == r, "expected 0, got %d\n", r);
6992 test_dm_messages(0, 0, 1);
6994 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6995 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6996 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6998 memset(&dm_messages, 0, sizeof(dm_messages));
6999 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7000 ok(0 == r, "expected 0, got %d\n", r);
7001 test_dm_messages(0, 1, 1);
7003 DestroyWindow(hwRichEdit);
7005 /* Check messages from richedit(ES_WANTRETURN) */
7007 hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7009 memset(&dm_messages, 0, sizeof(dm_messages));
7010 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7011 ok(0 == r, "expected 0, got %d\n", r);
7012 test_dm_messages(0, 0, 0);
7014 memset(&dm_messages, 0, sizeof(dm_messages));
7015 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7016 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7017 test_dm_messages(0, 0, 0);
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 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7025 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7026 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7028 memset(&dm_messages, 0, sizeof(dm_messages));
7029 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7030 ok(0 == r, "expected 0, got %d\n", r);
7031 test_dm_messages(0, 0, 0);
7033 DestroyWindow(hwRichEdit);
7034 DestroyWindow(hwParent);
7037 START_TEST( editor )
7039 BOOL ret;
7040 /* Must explicitly LoadLibrary(). The test has no references to functions in
7041 * RICHED20.DLL, so the linker doesn't actually link to it. */
7042 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
7043 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7045 test_WM_CHAR();
7046 test_EM_FINDTEXT();
7047 test_EM_GETLINE();
7048 test_EM_POSFROMCHAR();
7049 test_EM_SCROLLCARET();
7050 test_EM_SCROLL();
7051 test_scrollbar_visibility();
7052 test_WM_SETTEXT();
7053 test_EM_LINELENGTH();
7054 test_EM_SETCHARFORMAT();
7055 test_EM_SETTEXTMODE();
7056 test_TM_PLAINTEXT();
7057 test_EM_SETOPTIONS();
7058 test_WM_GETTEXT();
7059 test_EM_GETTEXTRANGE();
7060 test_EM_GETSELTEXT();
7061 test_EM_SETUNDOLIMIT();
7062 test_ES_PASSWORD();
7063 test_EM_SETTEXTEX();
7064 test_EM_LIMITTEXT();
7065 test_EM_EXLIMITTEXT();
7066 test_EM_GETLIMITTEXT();
7067 test_WM_SETFONT();
7068 test_EM_GETMODIFY();
7069 test_EM_EXSETSEL();
7070 test_WM_PASTE();
7071 test_EM_STREAMIN();
7072 test_EM_STREAMOUT();
7073 test_EM_STREAMOUT_FONTTBL();
7074 test_EM_StreamIn_Undo();
7075 test_EM_FORMATRANGE();
7076 test_unicode_conversions();
7077 test_EM_GETTEXTLENGTHEX();
7078 test_EM_REPLACESEL(1);
7079 test_EM_REPLACESEL(0);
7080 test_WM_NOTIFY();
7081 test_EM_AUTOURLDETECT();
7082 test_eventMask();
7083 test_undo_coalescing();
7084 test_word_movement();
7085 test_EM_CHARFROMPOS();
7086 test_SETPARAFORMAT();
7087 test_word_wrap();
7088 test_autoscroll();
7089 test_format_rect();
7090 test_WM_GETDLGCODE();
7091 test_zoom();
7092 test_dialogmode();
7094 /* Set the environment variable WINETEST_RICHED20 to keep windows
7095 * responsive and open for 30 seconds. This is useful for debugging.
7097 if (getenv( "WINETEST_RICHED20" )) {
7098 keep_responsive(30);
7101 OleFlushClipboard();
7102 ret = FreeLibrary(hmoduleRichEdit);
7103 ok(ret, "error: %d\n", (int) GetLastError());