push d1f5df181c120dbe494f7c89b454752c2e0dcc04
[wine/hacks.git] / dlls / riched20 / tests / editor.c
blobf8da3faf109a2eacbebcb9c628a00fe4f03c6c66
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 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
38 #define ok_w3(format, szString1, szString2, szString3) \
39 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
40 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
41 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
42 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
43 format, string1, string2, string3);
45 static HMODULE hmoduleRichEdit;
47 static int is_win9x = 0;
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 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 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, (WPARAM) SCF_DEFAULT,
295 (LPARAM) &cf2);
296 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
297 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
298 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
299 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
301 /* Haystack text, again */
302 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
303 sizeof(find_tests2)/sizeof(struct find_s));
305 /* Yet another range */
306 cf2.dwMask = CFM_BOLD | cf2.dwMask;
307 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
308 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
309 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
311 /* Haystack text, again */
312 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
313 sizeof(find_tests2)/sizeof(struct find_s));
315 DestroyWindow(hwndRichEdit);
318 static const struct getline_s {
319 int line;
320 size_t buffer_len;
321 const char *text;
322 } gl[] = {
323 {0, 10, "foo bar\r"},
324 {1, 10, "\r"},
325 {2, 10, "bar\r"},
326 {3, 10, "\r"},
328 /* Buffer smaller than line length */
329 {0, 2, "foo bar\r"},
330 {0, 1, "foo bar\r"},
331 {0, 0, "foo bar\r"}
334 static void test_EM_GETLINE(void)
336 int i;
337 HWND hwndRichEdit = new_richedit(NULL);
338 static const int nBuf = 1024;
339 char dest[1024], origdest[1024];
340 const char text[] = "foo bar\n"
341 "\n"
342 "bar\n";
344 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
346 memset(origdest, 0xBB, nBuf);
347 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
349 int nCopied;
350 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
351 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
352 memset(dest, 0xBB, nBuf);
353 *(WORD *) dest = gl[i].buffer_len;
355 /* EM_GETLINE appends a "\r\0" to the end of the line
356 * nCopied counts up to and including the '\r' */
357 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
358 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
359 expected_nCopied);
360 /* two special cases since a parameter is passed via dest */
361 if (gl[i].buffer_len == 0)
362 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
363 "buffer_len=0\n");
364 else if (gl[i].buffer_len == 1)
365 ok(dest[0] == gl[i].text[0] && !dest[1] &&
366 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
367 else
369 /* Prepare hex strings of buffers to dump on failure. */
370 char expectedbuf[1024];
371 char resultbuf[1024];
372 int j;
373 resultbuf[0] = '\0';
374 for (j = 0; j < 32; j++)
375 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
376 expectedbuf[0] = '\0';
377 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
378 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
379 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
380 sprintf(expectedbuf+strlen(expectedbuf), "??");
381 for (; j < 32; j++) /* Bytes after declared buffer size */
382 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
384 /* Test the part of the buffer that is expected to be written according
385 * to the MSDN documentation fo EM_GETLINE, which does not state that
386 * a NULL terminating character will be added unless no text is copied.
388 * Windows 95, 98 & NT do not append a NULL terminating character, but
389 * Windows 2000 and up do append a NULL terminating character if there
390 * is space in the buffer. The test will ignore this difference. */
391 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
392 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
393 i, expected_bytes_written, expectedbuf, resultbuf);
394 /* Test the part of the buffer after the declared length to make sure
395 * there are no buffer overruns. */
396 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
397 nBuf - gl[i].buffer_len),
398 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
399 i, expected_bytes_written, expectedbuf, resultbuf);
403 DestroyWindow(hwndRichEdit);
406 static void test_EM_LINELENGTH(void)
408 HWND hwndRichEdit = new_richedit(NULL);
409 const char * text =
410 "richedit1\r"
411 "richedit1\n"
412 "richedit1\r\n"
413 "richedit1";
414 int offset_test[10][2] = {
415 {0, 9},
416 {5, 9},
417 {10, 9},
418 {15, 9},
419 {20, 9},
420 {25, 9},
421 {30, 9},
422 {35, 9},
423 {40, 0},
424 {45, 0},
426 int i;
427 LRESULT result;
429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
431 for (i = 0; i < 10; i++) {
432 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
433 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
434 offset_test[i][0], result, offset_test[i][1]);
437 DestroyWindow(hwndRichEdit);
440 static int get_scroll_pos_y(HWND hwnd)
442 POINT p = {-1, -1};
443 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
444 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
445 return p.y;
448 static void move_cursor(HWND hwnd, long charindex)
450 CHARRANGE cr;
451 cr.cpMax = charindex;
452 cr.cpMin = charindex;
453 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
456 static void line_scroll(HWND hwnd, int amount)
458 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
461 static void test_EM_SCROLLCARET(void)
463 int prevY, curY;
464 const char text[] = "aa\n"
465 "this is a long line of text that should be longer than the "
466 "control's width\n"
467 "cc\n"
468 "dd\n"
469 "ee\n"
470 "ff\n"
471 "gg\n"
472 "hh\n";
473 /* The richedit window height needs to be large enough vertically to fit in
474 * more than two lines of text, so the new_richedit function can't be used
475 * since a height of 60 was not large enough on some systems.
477 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
478 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
479 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
480 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
482 /* Can't verify this */
483 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
485 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
487 /* Caret above visible window */
488 line_scroll(hwndRichEdit, 3);
489 prevY = get_scroll_pos_y(hwndRichEdit);
490 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
491 curY = get_scroll_pos_y(hwndRichEdit);
492 ok(prevY != curY, "%d == %d\n", prevY, curY);
494 /* Caret below visible window */
495 move_cursor(hwndRichEdit, sizeof(text) - 1);
496 line_scroll(hwndRichEdit, -3);
497 prevY = get_scroll_pos_y(hwndRichEdit);
498 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
499 curY = get_scroll_pos_y(hwndRichEdit);
500 ok(prevY != curY, "%d == %d\n", prevY, curY);
502 /* Caret in visible window */
503 move_cursor(hwndRichEdit, sizeof(text) - 2);
504 prevY = get_scroll_pos_y(hwndRichEdit);
505 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
506 curY = get_scroll_pos_y(hwndRichEdit);
507 ok(prevY == curY, "%d != %d\n", prevY, curY);
509 /* Caret still in visible window */
510 line_scroll(hwndRichEdit, -1);
511 prevY = get_scroll_pos_y(hwndRichEdit);
512 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
513 curY = get_scroll_pos_y(hwndRichEdit);
514 ok(prevY == curY, "%d != %d\n", prevY, curY);
516 DestroyWindow(hwndRichEdit);
519 static void test_EM_POSFROMCHAR(void)
521 HWND hwndRichEdit = new_richedit(NULL);
522 int i;
523 LRESULT result;
524 unsigned int height = 0;
525 int xpos = 0;
526 POINTL pt;
527 static const char text[] = "aa\n"
528 "this is a long line of text that should be longer than the "
529 "control's width\n"
530 "cc\n"
531 "dd\n"
532 "ee\n"
533 "ff\n"
534 "gg\n"
535 "hh\n";
537 /* Fill the control to lines to ensure that most of them are offscreen */
538 for (i = 0; i < 50; i++)
540 /* Do not modify the string; it is exactly 16 characters long. */
541 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
542 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
546 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
547 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
548 Richedit 3.0 accepts either of the above API conventions.
551 /* Testing Richedit 2.0 API format */
553 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
554 Since all lines are identical and drawn with the same font,
555 they should have the same height... right?
557 for (i = 0; i < 50; i++)
559 /* All the lines are 16 characters long */
560 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
561 if (i == 0)
563 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
564 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
565 xpos = LOWORD(result);
567 else if (i == 1)
569 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
570 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
571 height = HIWORD(result);
573 else
575 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
576 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
580 /* Testing position at end of text */
581 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
582 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
583 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
585 /* Testing position way past end of text */
586 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 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 that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
591 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
592 for (i = 0; i < 50; i++)
594 /* All the lines are 16 characters long */
595 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
596 ok((signed short)(HIWORD(result)) == (i - 1) * height,
597 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
598 (signed short)(HIWORD(result)), (i - 1) * height);
599 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
602 /* Testing position at end of text */
603 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
604 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
605 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
607 /* Testing position way past end of text */
608 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
609 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
610 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
612 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
614 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
616 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
617 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
618 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
619 xpos = LOWORD(result);
621 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
622 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
623 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
624 ok((signed short)(LOWORD(result)) < xpos,
625 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
626 (signed short)(LOWORD(result)), xpos);
627 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
629 /* Test around end of text that doesn't end in a newline. */
630 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
631 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
632 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
633 ok(pt.x > 1, "pt.x = %d\n", pt.x);
634 xpos = pt.x;
635 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
636 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
637 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
638 xpos = pt.x;
639 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
640 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
641 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
643 /* Try a negative position. */
644 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
645 ok(pt.x == 1, "pt.x = %d\n", pt.x);
647 DestroyWindow(hwndRichEdit);
650 static void test_EM_SETCHARFORMAT(void)
652 HWND hwndRichEdit = new_richedit(NULL);
653 CHARFORMAT2 cf2;
654 int rc = 0;
655 int tested_effects[] = {
656 CFE_BOLD,
657 CFE_ITALIC,
658 CFE_UNDERLINE,
659 CFE_STRIKEOUT,
660 CFE_PROTECTED,
661 CFE_LINK,
662 CFE_SUBSCRIPT,
663 CFE_SUPERSCRIPT,
666 int i;
667 CHARRANGE cr;
669 /* Invalid flags, CHARFORMAT2 structure blanked out */
670 memset(&cf2, 0, sizeof(cf2));
671 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
672 (LPARAM) &cf2);
673 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
675 /* A valid flag, CHARFORMAT2 structure blanked out */
676 memset(&cf2, 0, sizeof(cf2));
677 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
678 (LPARAM) &cf2);
679 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
681 /* A valid flag, CHARFORMAT2 structure blanked out */
682 memset(&cf2, 0, sizeof(cf2));
683 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
684 (LPARAM) &cf2);
685 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
687 /* A valid flag, CHARFORMAT2 structure blanked out */
688 memset(&cf2, 0, sizeof(cf2));
689 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
690 (LPARAM) &cf2);
691 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
693 /* A valid flag, CHARFORMAT2 structure blanked out */
694 memset(&cf2, 0, sizeof(cf2));
695 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
696 (LPARAM) &cf2);
697 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
699 /* Invalid flags, CHARFORMAT2 structure minimally filled */
700 memset(&cf2, 0, sizeof(cf2));
701 cf2.cbSize = sizeof(CHARFORMAT2);
702 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
703 (LPARAM) &cf2);
704 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
705 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
706 ok(rc == FALSE, "Should not be able to undo here.\n");
707 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
709 /* A valid flag, CHARFORMAT2 structure minimally filled */
710 memset(&cf2, 0, sizeof(cf2));
711 cf2.cbSize = sizeof(CHARFORMAT2);
712 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
713 (LPARAM) &cf2);
714 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
715 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
716 ok(rc == FALSE, "Should not be able to undo here.\n");
717 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
719 /* A valid flag, CHARFORMAT2 structure minimally filled */
720 memset(&cf2, 0, sizeof(cf2));
721 cf2.cbSize = sizeof(CHARFORMAT2);
722 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
723 (LPARAM) &cf2);
724 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
725 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
726 ok(rc == FALSE, "Should not be able to undo here.\n");
727 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
729 /* A valid flag, CHARFORMAT2 structure minimally filled */
730 memset(&cf2, 0, sizeof(cf2));
731 cf2.cbSize = sizeof(CHARFORMAT2);
732 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
733 (LPARAM) &cf2);
734 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
735 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
736 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
737 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
739 /* A valid flag, CHARFORMAT2 structure minimally filled */
740 memset(&cf2, 0, sizeof(cf2));
741 cf2.cbSize = sizeof(CHARFORMAT2);
742 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
743 (LPARAM) &cf2);
744 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
745 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
746 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
747 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
749 cf2.cbSize = sizeof(CHARFORMAT2);
750 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
751 (LPARAM) &cf2);
753 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
754 cf2.cbSize = sizeof(CHARFORMAT2);
755 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
756 (LPARAM) &cf2);
757 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
758 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
760 /* wParam==0 is default char format, does not set modify */
761 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
762 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
763 ok(rc == 0, "Text marked as modified, expected not modified!\n");
764 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
765 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
766 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
767 ok(rc == 0, "Text marked as modified, expected not modified!\n");
769 /* wParam==SCF_SELECTION sets modify if nonempty selection */
770 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
771 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
772 ok(rc == 0, "Text marked as modified, expected not modified!\n");
773 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
774 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
775 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
776 ok(rc == 0, "Text marked as modified, expected not modified!\n");
778 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
779 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
780 ok(rc == 0, "Text marked as modified, expected not modified!\n");
781 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
782 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
783 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
784 ok(rc == 0, "Text marked as modified, expected not modified!\n");
785 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
786 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
787 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
788 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
789 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
791 /* wParam==SCF_ALL sets modify regardless of whether text is present */
792 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
793 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
794 ok(rc == 0, "Text marked as modified, expected not modified!\n");
795 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
796 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
797 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
798 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
800 DestroyWindow(hwndRichEdit);
802 /* EM_GETCHARFORMAT tests */
803 for (i = 0; tested_effects[i]; i++)
805 hwndRichEdit = new_richedit(NULL);
806 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
808 /* Need to set a TrueType font to get consistent CFM_BOLD results */
809 memset(&cf2, 0, sizeof(CHARFORMAT2));
810 cf2.cbSize = sizeof(CHARFORMAT2);
811 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
812 cf2.dwEffects = 0;
813 strcpy(cf2.szFaceName, "Courier New");
814 cf2.wWeight = FW_DONTCARE;
815 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
817 memset(&cf2, 0, sizeof(CHARFORMAT2));
818 cf2.cbSize = sizeof(CHARFORMAT2);
819 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
820 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
821 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
822 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
824 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
825 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
826 ok((cf2.dwEffects & tested_effects[i]) == 0,
827 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
829 memset(&cf2, 0, sizeof(CHARFORMAT2));
830 cf2.cbSize = sizeof(CHARFORMAT2);
831 cf2.dwMask = tested_effects[i];
832 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
833 cf2.dwMask = CFM_SUPERSCRIPT;
834 cf2.dwEffects = tested_effects[i];
835 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
836 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
838 memset(&cf2, 0, sizeof(CHARFORMAT2));
839 cf2.cbSize = sizeof(CHARFORMAT2);
840 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
841 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
842 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
843 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
845 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
846 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
847 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
848 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
850 memset(&cf2, 0, sizeof(CHARFORMAT2));
851 cf2.cbSize = sizeof(CHARFORMAT2);
852 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
853 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
854 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
855 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
857 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
858 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
859 ok((cf2.dwEffects & tested_effects[i]) == 0,
860 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
862 memset(&cf2, 0, sizeof(CHARFORMAT2));
863 cf2.cbSize = sizeof(CHARFORMAT2);
864 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
865 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
866 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
867 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
869 (cf2.dwMask & tested_effects[i]) == 0),
870 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
872 DestroyWindow(hwndRichEdit);
875 for (i = 0; tested_effects[i]; i++)
877 hwndRichEdit = new_richedit(NULL);
878 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
880 /* Need to set a TrueType font to get consistent CFM_BOLD results */
881 memset(&cf2, 0, sizeof(CHARFORMAT2));
882 cf2.cbSize = sizeof(CHARFORMAT2);
883 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
884 cf2.dwEffects = 0;
885 strcpy(cf2.szFaceName, "Courier New");
886 cf2.wWeight = FW_DONTCARE;
887 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
889 memset(&cf2, 0, sizeof(CHARFORMAT2));
890 cf2.cbSize = sizeof(CHARFORMAT2);
891 cf2.dwMask = tested_effects[i];
892 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
893 cf2.dwMask = CFM_SUPERSCRIPT;
894 cf2.dwEffects = tested_effects[i];
895 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
896 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
898 memset(&cf2, 0, sizeof(CHARFORMAT2));
899 cf2.cbSize = sizeof(CHARFORMAT2);
900 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
901 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
902 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
903 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
905 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
906 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
907 ok((cf2.dwEffects & tested_effects[i]) == 0,
908 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
910 memset(&cf2, 0, sizeof(CHARFORMAT2));
911 cf2.cbSize = sizeof(CHARFORMAT2);
912 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
913 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
914 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
915 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
917 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
918 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
919 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
920 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
922 memset(&cf2, 0, sizeof(CHARFORMAT2));
923 cf2.cbSize = sizeof(CHARFORMAT2);
924 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
925 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
926 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
927 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
929 (cf2.dwMask & tested_effects[i]) == 0),
930 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
931 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
932 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
934 DestroyWindow(hwndRichEdit);
937 /* Effects applied on an empty selection should take effect when selection is
938 replaced with text */
939 hwndRichEdit = new_richedit(NULL);
940 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
941 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
943 memset(&cf2, 0, sizeof(CHARFORMAT2));
944 cf2.cbSize = sizeof(CHARFORMAT2);
945 cf2.dwMask = CFM_BOLD;
946 cf2.dwEffects = CFE_BOLD;
947 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
949 /* Selection is now nonempty */
950 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
952 memset(&cf2, 0, sizeof(CHARFORMAT2));
953 cf2.cbSize = sizeof(CHARFORMAT2);
954 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
955 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
957 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
958 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
959 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
960 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
963 /* Set two effects on an empty selection */
964 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
965 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
967 memset(&cf2, 0, sizeof(CHARFORMAT2));
968 cf2.cbSize = sizeof(CHARFORMAT2);
969 cf2.dwMask = CFM_BOLD;
970 cf2.dwEffects = CFE_BOLD;
971 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
972 cf2.dwMask = CFM_ITALIC;
973 cf2.dwEffects = CFE_ITALIC;
974 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
976 /* Selection is now nonempty */
977 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
979 memset(&cf2, 0, sizeof(CHARFORMAT2));
980 cf2.cbSize = sizeof(CHARFORMAT2);
981 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
982 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
984 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
985 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
986 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
987 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
989 /* Setting the (empty) selection to exactly the same place as before should
990 NOT clear the insertion style! */
991 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
992 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
994 memset(&cf2, 0, sizeof(CHARFORMAT2));
995 cf2.cbSize = sizeof(CHARFORMAT2);
996 cf2.dwMask = CFM_BOLD;
997 cf2.dwEffects = CFE_BOLD;
998 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1000 /* Empty selection in same place, insert style should NOT be forgotten here. */
1001 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1003 /* Selection is now nonempty */
1004 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1006 memset(&cf2, 0, sizeof(CHARFORMAT2));
1007 cf2.cbSize = sizeof(CHARFORMAT2);
1008 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1009 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1011 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1012 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1013 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1014 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1016 /* Ditto with EM_EXSETSEL */
1017 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1018 cr.cpMin = 2; cr.cpMax = 2;
1019 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1021 memset(&cf2, 0, sizeof(CHARFORMAT2));
1022 cf2.cbSize = sizeof(CHARFORMAT2);
1023 cf2.dwMask = CFM_BOLD;
1024 cf2.dwEffects = CFE_BOLD;
1025 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1027 /* Empty selection in same place, insert style should NOT be forgotten here. */
1028 cr.cpMin = 2; cr.cpMax = 2;
1029 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1031 /* Selection is now nonempty */
1032 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1034 memset(&cf2, 0, sizeof(CHARFORMAT2));
1035 cf2.cbSize = sizeof(CHARFORMAT2);
1036 cr.cpMin = 2; cr.cpMax = 6;
1037 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1038 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1040 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1041 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1042 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1043 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1045 DestroyWindow(hwndRichEdit);
1048 static void test_EM_SETTEXTMODE(void)
1050 HWND hwndRichEdit = new_richedit(NULL);
1051 CHARFORMAT2 cf2, cf2test;
1052 CHARRANGE cr;
1053 int rc = 0;
1055 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1056 /*Insert text into the control*/
1058 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1060 /*Attempt to change the control to plain text mode*/
1061 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1062 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1064 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1065 If rich text is pasted, it should have the same formatting as the rest
1066 of the text in the control*/
1068 /*Italicize the text
1069 *NOTE: If the default text was already italicized, the test will simply
1070 reverse; in other words, it will copy a regular "wine" into a plain
1071 text window that uses an italicized format*/
1072 cf2.cbSize = sizeof(CHARFORMAT2);
1073 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1074 (LPARAM) &cf2);
1076 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1077 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1079 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1080 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1082 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1083 however, SCF_ALL has been implemented*/
1084 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1085 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1087 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1088 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1090 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1092 /*Select the string "wine"*/
1093 cr.cpMin = 0;
1094 cr.cpMax = 4;
1095 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1097 /*Copy the italicized "wine" to the clipboard*/
1098 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1100 /*Reset the formatting to default*/
1101 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1102 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1103 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1105 /*Clear the text in the control*/
1106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1108 /*Switch to Plain Text Mode*/
1109 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1110 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1112 /*Input "wine" again in normal format*/
1113 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1115 /*Paste the italicized "wine" into the control*/
1116 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1118 /*Select a character from the first "wine" string*/
1119 cr.cpMin = 2;
1120 cr.cpMax = 3;
1121 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1123 /*Retrieve its formatting*/
1124 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1125 (LPARAM) &cf2);
1127 /*Select a character from the second "wine" string*/
1128 cr.cpMin = 5;
1129 cr.cpMax = 6;
1130 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1132 /*Retrieve its formatting*/
1133 cf2test.cbSize = sizeof(CHARFORMAT2);
1134 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1135 (LPARAM) &cf2test);
1137 /*Compare the two formattings*/
1138 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1139 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1140 cf2.dwEffects, cf2test.dwEffects);
1141 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1142 printing "wine" in the current format(normal)
1143 pasting "wine" from the clipboard(italicized)
1144 comparing the two formats(should differ)*/
1146 /*Attempt to switch with text in control*/
1147 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1148 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1150 /*Clear control*/
1151 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1153 /*Switch into Rich Text mode*/
1154 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1155 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1157 /*Print "wine" in normal formatting into the control*/
1158 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1160 /*Paste italicized "wine" into the control*/
1161 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1163 /*Select text from the first "wine" string*/
1164 cr.cpMin = 1;
1165 cr.cpMax = 3;
1166 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1168 /*Retrieve its formatting*/
1169 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1170 (LPARAM) &cf2);
1172 /*Select text from the second "wine" string*/
1173 cr.cpMin = 6;
1174 cr.cpMax = 7;
1175 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1177 /*Retrieve its formatting*/
1178 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1179 (LPARAM) &cf2test);
1181 /*Test that the two formattings are not the same*/
1182 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1183 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1184 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1186 DestroyWindow(hwndRichEdit);
1189 static void test_SETPARAFORMAT(void)
1191 HWND hwndRichEdit = new_richedit(NULL);
1192 PARAFORMAT2 fmt;
1193 HRESULT ret;
1194 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1195 fmt.cbSize = sizeof(PARAFORMAT2);
1196 fmt.dwMask = PFM_ALIGNMENT;
1197 fmt.wAlignment = PFA_LEFT;
1199 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1200 ok(ret != 0, "expected non-zero got %d\n", ret);
1202 fmt.cbSize = sizeof(PARAFORMAT2);
1203 fmt.dwMask = -1;
1204 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1205 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1206 * between richedit different native builds of riched20.dll
1207 * used on different Windows versions. */
1208 ret &= ~PFM_TABLEROWDELIMITER;
1209 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1211 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1212 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1214 DestroyWindow(hwndRichEdit);
1217 static void test_TM_PLAINTEXT(void)
1219 /*Tests plain text properties*/
1221 HWND hwndRichEdit = new_richedit(NULL);
1222 CHARFORMAT2 cf2, cf2test;
1223 CHARRANGE cr;
1224 int rc = 0;
1226 /*Switch to plain text mode*/
1228 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1229 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1231 /*Fill control with text*/
1233 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1235 /*Select some text and bold it*/
1237 cr.cpMin = 10;
1238 cr.cpMax = 20;
1239 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1240 cf2.cbSize = sizeof(CHARFORMAT2);
1241 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1242 (LPARAM) &cf2);
1244 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1245 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1247 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1248 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1250 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1251 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1253 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1254 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1256 /*Get the formatting of those characters*/
1258 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1260 /*Get the formatting of some other characters*/
1261 cf2test.cbSize = sizeof(CHARFORMAT2);
1262 cr.cpMin = 21;
1263 cr.cpMax = 30;
1264 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1265 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1267 /*Test that they are the same as plain text allows only one formatting*/
1269 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1270 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1271 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1273 /*Fill the control with a "wine" string, which when inserted will be bold*/
1275 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1277 /*Copy the bolded "wine" string*/
1279 cr.cpMin = 0;
1280 cr.cpMax = 4;
1281 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1282 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1284 /*Swap back to rich text*/
1286 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1287 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1289 /*Set the default formatting to bold italics*/
1291 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1292 cf2.dwMask |= CFM_ITALIC;
1293 cf2.dwEffects ^= CFE_ITALIC;
1294 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1295 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1297 /*Set the text in the control to "wine", which will be bold and italicized*/
1299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1301 /*Paste the plain text "wine" string, which should take the insert
1302 formatting, which at the moment is bold italics*/
1304 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1306 /*Select the first "wine" string and retrieve its formatting*/
1308 cr.cpMin = 1;
1309 cr.cpMax = 3;
1310 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1311 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1313 /*Select the second "wine" string and retrieve its formatting*/
1315 cr.cpMin = 5;
1316 cr.cpMax = 7;
1317 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1318 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1320 /*Compare the two formattings. They should be the same.*/
1322 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1323 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1324 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1325 DestroyWindow(hwndRichEdit);
1328 static void test_WM_GETTEXT(void)
1330 HWND hwndRichEdit = new_richedit(NULL);
1331 static const char text[] = "Hello. My name is RichEdit!";
1332 static const char text2[] = "Hello. My name is RichEdit!\r";
1333 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1334 char buffer[1024] = {0};
1335 int result;
1337 /* Baseline test with normal-sized buffer */
1338 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1339 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1340 ok(result == lstrlen(buffer),
1341 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1342 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1343 result = strcmp(buffer,text);
1344 ok(result == 0,
1345 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1347 /* Test for returned value of WM_GETTEXTLENGTH */
1348 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1349 ok(result == lstrlen(text),
1350 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1351 result, lstrlen(text));
1353 /* Test for behavior in overflow case */
1354 memset(buffer, 0, 1024);
1355 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1356 ok(result == 0 ||
1357 result == lstrlenA(text) - 1, /* XP, win2k3 */
1358 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1359 result = strcmp(buffer,text);
1360 if (result)
1361 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1362 ok(result == 0,
1363 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1365 /* Baseline test with normal-sized buffer and carriage return */
1366 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1367 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1368 ok(result == lstrlen(buffer),
1369 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1370 result = strcmp(buffer,text2_after);
1371 ok(result == 0,
1372 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1374 /* Test for returned value of WM_GETTEXTLENGTH */
1375 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1376 ok(result == lstrlen(text2_after),
1377 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1378 result, lstrlen(text2_after));
1380 /* Test for behavior of CRLF conversion in case of overflow */
1381 memset(buffer, 0, 1024);
1382 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1383 ok(result == 0 ||
1384 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1385 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1386 result = strcmp(buffer,text2);
1387 if (result)
1388 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1389 ok(result == 0,
1390 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1392 DestroyWindow(hwndRichEdit);
1395 static void test_EM_GETTEXTRANGE(void)
1397 HWND hwndRichEdit = new_richedit(NULL);
1398 const char * text1 = "foo bar\r\nfoo bar";
1399 const char * text2 = "foo bar\rfoo bar";
1400 const char * expect = "bar\rfoo";
1401 char buffer[1024] = {0};
1402 LRESULT result;
1403 TEXTRANGEA textRange;
1405 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1407 textRange.lpstrText = buffer;
1408 textRange.chrg.cpMin = 4;
1409 textRange.chrg.cpMax = 11;
1410 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1411 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1412 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1414 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1416 textRange.lpstrText = buffer;
1417 textRange.chrg.cpMin = 4;
1418 textRange.chrg.cpMax = 11;
1419 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1420 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1421 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1423 DestroyWindow(hwndRichEdit);
1426 static void test_EM_GETSELTEXT(void)
1428 HWND hwndRichEdit = new_richedit(NULL);
1429 const char * text1 = "foo bar\r\nfoo bar";
1430 const char * text2 = "foo bar\rfoo bar";
1431 const char * expect = "bar\rfoo";
1432 char buffer[1024] = {0};
1433 LRESULT result;
1435 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1437 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1438 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1439 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1440 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1442 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1444 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1445 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1446 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1447 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1449 DestroyWindow(hwndRichEdit);
1452 /* FIXME: need to test unimplemented options and robustly test wparam */
1453 static void test_EM_SETOPTIONS(void)
1455 HWND hwndRichEdit;
1456 static const char text[] = "Hello. My name is RichEdit!";
1457 char buffer[1024] = {0};
1458 DWORD dwStyle, options, oldOptions;
1459 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1460 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1461 ES_SELECTIONBAR|ES_VERTICAL;
1463 /* Test initial options. */
1464 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1465 0, 0, 200, 60, NULL, NULL,
1466 hmoduleRichEdit, NULL);
1467 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1468 RICHEDIT_CLASS, (int) GetLastError());
1469 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1470 ok(options == 0, "Incorrect initial options %x\n", options);
1471 DestroyWindow(hwndRichEdit);
1473 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1474 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1475 0, 0, 200, 60, NULL, NULL,
1476 hmoduleRichEdit, NULL);
1477 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1478 RICHEDIT_CLASS, (int) GetLastError());
1479 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1480 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1481 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1482 "Incorrect initial options %x\n", options);
1484 /* NEGATIVE TESTING - NO OPTIONS SET */
1485 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1486 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1488 /* testing no readonly by sending 'a' to the control*/
1489 SetFocus(hwndRichEdit);
1490 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1491 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1492 ok(buffer[0]=='a',
1493 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1494 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1496 /* READONLY - sending 'a' to the control */
1497 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1498 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1499 SetFocus(hwndRichEdit);
1500 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1501 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1502 ok(buffer[0]==text[0],
1503 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1505 /* EM_SETOPTIONS changes the window style, but changing the
1506 * window style does not change the options. */
1507 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1508 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1509 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1510 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1511 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1512 /* Confirm that the text is still read only. */
1513 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1514 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1515 ok(buffer[0]==text[0],
1516 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1518 oldOptions = options;
1519 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1520 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1521 ok(options == oldOptions,
1522 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1524 DestroyWindow(hwndRichEdit);
1527 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1529 CHARFORMAT2W text_format;
1530 text_format.cbSize = sizeof(text_format);
1531 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1532 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1533 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1536 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1538 int link_present = 0;
1540 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1541 if (is_url)
1542 { /* control text is url; should get CFE_LINK */
1543 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1545 else
1547 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1551 static HWND new_static_wnd(HWND parent) {
1552 return new_window("Static", 0, parent);
1555 static void test_EM_AUTOURLDETECT(void)
1557 /* DO NOT change the properties of the first two elements. To shorten the
1558 tests, all tests after WM_SETTEXT test just the first two elements -
1559 one non-URL and one URL */
1560 struct urls_s {
1561 const char *text;
1562 int is_url;
1563 } urls[12] = {
1564 {"winehq.org", 0},
1565 {"http://www.winehq.org", 1},
1566 {"http//winehq.org", 0},
1567 {"ww.winehq.org", 0},
1568 {"www.winehq.org", 1},
1569 {"ftp://192.168.1.1", 1},
1570 {"ftp//192.168.1.1", 0},
1571 {"mailto:your@email.com", 1},
1572 {"prospero:prosperoserver", 1},
1573 {"telnet:test", 1},
1574 {"news:newserver", 1},
1575 {"wais:waisserver", 1}
1578 int i, j;
1579 int urlRet=-1;
1580 HWND hwndRichEdit, parent;
1582 /* All of the following should cause the URL to be detected */
1583 const char * templates_delim[] = {
1584 "This is some text with X on it",
1585 "This is some text with (X) on it",
1586 "This is some text with X\r on it",
1587 "This is some text with ---X--- on it",
1588 "This is some text with \"X\" on it",
1589 "This is some text with 'X' on it",
1590 "This is some text with 'X' on it",
1591 "This is some text with :X: on it",
1593 "This text ends with X",
1595 "This is some text with X) on it",
1596 "This is some text with X--- on it",
1597 "This is some text with X\" on it",
1598 "This is some text with X' on it",
1599 "This is some text with X: on it",
1601 "This is some text with (X on it",
1602 "This is some text with \rX on it",
1603 "This is some text with ---X on it",
1604 "This is some text with \"X on it",
1605 "This is some text with 'X on it",
1606 "This is some text with :X on it",
1608 /* None of these should cause the URL to be detected */
1609 const char * templates_non_delim[] = {
1610 "This is some text with |X| on it",
1611 "This is some text with *X* on it",
1612 "This is some text with /X/ on it",
1613 "This is some text with +X+ on it",
1614 "This is some text with %X% on it",
1615 "This is some text with #X# on it",
1616 "This is some text with @X@ on it",
1617 "This is some text with \\X\\ on it",
1618 "This is some text with |X on it",
1619 "This is some text with *X on it",
1620 "This is some text with /X on it",
1621 "This is some text with +X on it",
1622 "This is some text with %X on it",
1623 "This is some text with #X on it",
1624 "This is some text with @X on it",
1625 "This is some text with \\X on it",
1627 /* All of these cause the URL detection to be extended by one more byte,
1628 thus demonstrating that the tested character is considered as part
1629 of the URL. */
1630 const char * templates_xten_delim[] = {
1631 "This is some text with X| on it",
1632 "This is some text with X* on it",
1633 "This is some text with X/ on it",
1634 "This is some text with X+ on it",
1635 "This is some text with X% on it",
1636 "This is some text with X# on it",
1637 "This is some text with X@ on it",
1638 "This is some text with X\\ on it",
1640 char buffer[1024];
1642 parent = new_static_wnd(NULL);
1643 hwndRichEdit = new_richedit(parent);
1644 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1645 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1646 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1647 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1648 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1649 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1650 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1651 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1652 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1653 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1654 /* for each url, check the text to see if CFE_LINK effect is present */
1655 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1657 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1658 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1659 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1661 /* Link detection should happen immediately upon WM_SETTEXT */
1662 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1663 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1664 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1666 DestroyWindow(hwndRichEdit);
1668 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1669 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1670 hwndRichEdit = new_richedit(parent);
1672 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1673 char * at_pos;
1674 int at_offset;
1675 int end_offset;
1677 at_pos = strchr(templates_delim[j], 'X');
1678 at_offset = at_pos - templates_delim[j];
1679 strncpy(buffer, templates_delim[j], at_offset);
1680 buffer[at_offset] = '\0';
1681 strcat(buffer, urls[i].text);
1682 strcat(buffer, templates_delim[j] + at_offset + 1);
1683 end_offset = at_offset + strlen(urls[i].text);
1685 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1686 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1688 /* This assumes no templates start with the URL itself, and that they
1689 have at least two characters before the URL text */
1690 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1691 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1692 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1693 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1694 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1695 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1697 if (urls[i].is_url)
1699 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1700 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1701 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1702 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1704 else
1706 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1707 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1708 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1711 if (buffer[end_offset] != '\0')
1713 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1714 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1715 if (buffer[end_offset +1] != '\0')
1717 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1718 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1723 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1724 char * at_pos;
1725 int at_offset;
1726 int end_offset;
1728 at_pos = strchr(templates_non_delim[j], 'X');
1729 at_offset = at_pos - templates_non_delim[j];
1730 strncpy(buffer, templates_non_delim[j], at_offset);
1731 buffer[at_offset] = '\0';
1732 strcat(buffer, urls[i].text);
1733 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1734 end_offset = at_offset + strlen(urls[i].text);
1736 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1737 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1739 /* This assumes no templates start with the URL itself, and that they
1740 have at least two characters before the URL text */
1741 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1742 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1743 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1744 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1745 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1746 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1748 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1749 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1750 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1751 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1752 if (buffer[end_offset] != '\0')
1754 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1755 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1756 if (buffer[end_offset +1] != '\0')
1758 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1759 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1764 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1765 char * at_pos;
1766 int at_offset;
1767 int end_offset;
1769 at_pos = strchr(templates_xten_delim[j], 'X');
1770 at_offset = at_pos - templates_xten_delim[j];
1771 strncpy(buffer, templates_xten_delim[j], at_offset);
1772 buffer[at_offset] = '\0';
1773 strcat(buffer, urls[i].text);
1774 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1775 end_offset = at_offset + strlen(urls[i].text);
1777 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1778 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1780 /* This assumes no templates start with the URL itself, and that they
1781 have at least two characters before the URL text */
1782 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1783 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1784 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1785 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1786 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1787 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1789 if (urls[i].is_url)
1791 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1792 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1793 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1794 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1795 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1796 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1798 else
1800 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1801 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1802 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1803 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1804 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1805 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1807 if (buffer[end_offset +1] != '\0')
1809 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1810 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1811 if (buffer[end_offset +2] != '\0')
1813 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1814 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1819 DestroyWindow(hwndRichEdit);
1820 hwndRichEdit = NULL;
1823 /* Test detection of URLs within normal text - WM_CHAR case. */
1824 /* Test only the first two URL examples for brevity */
1825 for (i = 0; i < 2; i++) {
1826 hwndRichEdit = new_richedit(parent);
1828 /* Also for brevity, test only the first three delimiters */
1829 for (j = 0; j < 3; j++) {
1830 char * at_pos;
1831 int at_offset;
1832 int end_offset;
1833 int u, v;
1835 at_pos = strchr(templates_delim[j], 'X');
1836 at_offset = at_pos - templates_delim[j];
1837 end_offset = at_offset + strlen(urls[i].text);
1839 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1840 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1841 for (u = 0; templates_delim[j][u]; u++) {
1842 if (templates_delim[j][u] == '\r') {
1843 simulate_typing_characters(hwndRichEdit, "\r");
1844 } else if (templates_delim[j][u] != 'X') {
1845 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1846 } else {
1847 for (v = 0; urls[i].text[v]; v++) {
1848 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1852 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1854 /* This assumes no templates start with the URL itself, and that they
1855 have at least two characters before the URL text */
1856 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1857 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1858 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1859 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1860 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1861 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1863 if (urls[i].is_url)
1865 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1866 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1867 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1868 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1870 else
1872 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1873 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1874 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1875 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1877 if (buffer[end_offset] != '\0')
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1881 if (buffer[end_offset +1] != '\0')
1883 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1884 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1888 /* The following will insert a paragraph break after the first character
1889 of the URL candidate, thus breaking the URL. It is expected that the
1890 CFE_LINK attribute should break across both pieces of the URL */
1891 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1892 simulate_typing_characters(hwndRichEdit, "\r");
1893 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1895 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1896 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1897 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1898 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1899 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1900 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1902 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1903 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1904 /* end_offset moved because of paragraph break */
1905 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1906 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1907 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1908 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1910 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1911 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1912 if (buffer[end_offset +2] != '\0')
1914 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1915 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1919 /* The following will remove the just-inserted paragraph break, thus
1920 restoring the URL */
1921 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1922 simulate_typing_characters(hwndRichEdit, "\b");
1923 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1925 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1926 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1927 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1928 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1929 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1930 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1932 if (urls[i].is_url)
1934 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1935 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1936 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1937 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1939 else
1941 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1942 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1943 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1944 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1946 if (buffer[end_offset] != '\0')
1948 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1949 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1950 if (buffer[end_offset +1] != '\0')
1952 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1953 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1957 DestroyWindow(hwndRichEdit);
1958 hwndRichEdit = NULL;
1961 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1962 /* Test just the first two URL examples for brevity */
1963 for (i = 0; i < 2; i++) {
1964 SETTEXTEX st;
1966 hwndRichEdit = new_richedit(parent);
1968 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1969 be detected:
1970 1) Set entire text, a la WM_SETTEXT
1971 2) Set a selection of the text to the URL
1972 3) Set a portion of the text at a time, which eventually results in
1973 an URL
1974 All of them should give equivalent results
1977 /* Set entire text in one go, like WM_SETTEXT */
1978 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1979 char * at_pos;
1980 int at_offset;
1981 int end_offset;
1983 st.codepage = CP_ACP;
1984 st.flags = ST_DEFAULT;
1986 at_pos = strchr(templates_delim[j], 'X');
1987 at_offset = at_pos - templates_delim[j];
1988 strncpy(buffer, templates_delim[j], at_offset);
1989 buffer[at_offset] = '\0';
1990 strcat(buffer, urls[i].text);
1991 strcat(buffer, templates_delim[j] + at_offset + 1);
1992 end_offset = at_offset + strlen(urls[i].text);
1994 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1995 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1997 /* This assumes no templates start with the URL itself, and that they
1998 have at least two characters before the URL text */
1999 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2000 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2003 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2004 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2006 if (urls[i].is_url)
2008 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2009 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2010 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2011 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2013 else
2015 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2016 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2017 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2018 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2020 if (buffer[end_offset] != '\0')
2022 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2023 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2024 if (buffer[end_offset +1] != '\0')
2026 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2027 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2032 /* Set selection with X to the URL */
2033 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2034 char * at_pos;
2035 int at_offset;
2036 int end_offset;
2038 at_pos = strchr(templates_delim[j], 'X');
2039 at_offset = at_pos - templates_delim[j];
2040 end_offset = at_offset + strlen(urls[i].text);
2042 st.codepage = CP_ACP;
2043 st.flags = ST_DEFAULT;
2044 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2045 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2046 st.flags = ST_SELECTION;
2047 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2048 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2049 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2051 /* This assumes no templates start with the URL itself, and that they
2052 have at least two characters before the URL text */
2053 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2054 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2057 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2058 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2060 if (urls[i].is_url)
2062 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2063 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2064 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2065 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2067 else
2069 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2070 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2071 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2072 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2074 if (buffer[end_offset] != '\0')
2076 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2077 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2078 if (buffer[end_offset +1] != '\0')
2080 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2081 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2086 /* Set selection with X to the first character of the URL, then the rest */
2087 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2088 char * at_pos;
2089 int at_offset;
2090 int end_offset;
2092 at_pos = strchr(templates_delim[j], 'X');
2093 at_offset = at_pos - templates_delim[j];
2094 end_offset = at_offset + strlen(urls[i].text);
2096 strcpy(buffer, "YY");
2097 buffer[0] = urls[i].text[0];
2099 st.codepage = CP_ACP;
2100 st.flags = ST_DEFAULT;
2101 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2102 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2103 st.flags = ST_SELECTION;
2104 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2105 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2106 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2107 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2108 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2110 /* This assumes no templates start with the URL itself, and that they
2111 have at least two characters before the URL text */
2112 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2113 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2114 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2115 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2116 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2117 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2119 if (urls[i].is_url)
2121 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2122 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2123 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2124 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2126 else
2128 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2129 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2130 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2131 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2133 if (buffer[end_offset] != '\0')
2135 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2136 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2137 if (buffer[end_offset +1] != '\0')
2139 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2140 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2145 DestroyWindow(hwndRichEdit);
2146 hwndRichEdit = NULL;
2149 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2150 /* Test just the first two URL examples for brevity */
2151 for (i = 0; i < 2; i++) {
2152 hwndRichEdit = new_richedit(parent);
2154 /* Set selection with X to the URL */
2155 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2156 char * at_pos;
2157 int at_offset;
2158 int end_offset;
2160 at_pos = strchr(templates_delim[j], 'X');
2161 at_offset = at_pos - templates_delim[j];
2162 end_offset = at_offset + strlen(urls[i].text);
2164 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2165 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2166 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2167 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2168 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2170 /* This assumes no templates start with the URL itself, and that they
2171 have at least two characters before the URL text */
2172 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2173 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2174 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2175 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2176 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2177 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2179 if (urls[i].is_url)
2181 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2182 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2183 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2184 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2186 else
2188 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2189 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2190 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2191 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2193 if (buffer[end_offset] != '\0')
2195 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2196 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2197 if (buffer[end_offset +1] != '\0')
2199 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2200 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2205 /* Set selection with X to the first character of the URL, then the rest */
2206 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2207 char * at_pos;
2208 int at_offset;
2209 int end_offset;
2211 at_pos = strchr(templates_delim[j], 'X');
2212 at_offset = at_pos - templates_delim[j];
2213 end_offset = at_offset + strlen(urls[i].text);
2215 strcpy(buffer, "YY");
2216 buffer[0] = urls[i].text[0];
2218 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2219 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2220 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2221 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2222 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2223 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2224 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2226 /* This assumes no templates start with the URL itself, and that they
2227 have at least two characters before the URL text */
2228 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2229 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2230 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2231 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2232 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2233 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2235 if (urls[i].is_url)
2237 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2238 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2239 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2240 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2242 else
2244 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2245 "CFE_LINK incorrectly 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 incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2249 if (buffer[end_offset] != '\0')
2251 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2252 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2253 if (buffer[end_offset +1] != '\0')
2255 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2256 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2261 DestroyWindow(hwndRichEdit);
2262 hwndRichEdit = NULL;
2265 DestroyWindow(parent);
2268 static void test_EM_SCROLL(void)
2270 int i, j;
2271 int r; /* return value */
2272 int expr; /* expected return value */
2273 HWND hwndRichEdit = new_richedit(NULL);
2274 int y_before, y_after; /* units of lines of text */
2276 /* test a richedit box containing a single line of text */
2277 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2278 expr = 0x00010000;
2279 for (i = 0; i < 4; i++) {
2280 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2282 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2283 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2284 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2285 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2286 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2287 "(i == %d)\n", y_after, i);
2291 * test a richedit box that will scroll. There are two general
2292 * cases: the case without any long lines and the case with a long
2293 * line.
2295 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2296 if (i == 0)
2297 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2298 else
2299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2300 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2301 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2302 "LONG LINE \nb\nc\nd\ne");
2303 for (j = 0; j < 12; j++) /* reset scroll position to top */
2304 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2306 /* get first visible line */
2307 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2308 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2310 /* get new current first visible line */
2311 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2313 ok(((r & 0xffffff00) == 0x00010000) &&
2314 ((r & 0x000000ff) != 0x00000000),
2315 "EM_SCROLL page down didn't scroll by a small positive number of "
2316 "lines (r == 0x%08x)\n", r);
2317 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2318 "(line %d scrolled to line %d\n", y_before, y_after);
2320 y_before = y_after;
2322 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2323 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2324 ok(((r & 0xffffff00) == 0x0001ff00),
2325 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2326 "(r == 0x%08x)\n", r);
2327 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2328 "%d scrolled to line %d\n", y_before, y_after);
2330 y_before = y_after;
2332 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2334 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2336 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2337 "(r == 0x%08x)\n", r);
2338 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2339 "1 line (%d scrolled to %d)\n", y_before, y_after);
2341 y_before = y_after;
2343 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2345 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2347 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2348 "(r == 0x%08x)\n", r);
2349 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2350 "line (%d scrolled to %d)\n", y_before, y_after);
2352 y_before = y_after;
2354 r = SendMessage(hwndRichEdit, EM_SCROLL,
2355 SB_LINEUP, 0); /* lineup beyond top */
2357 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2359 ok(r == 0x00010000,
2360 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2361 ok(y_before == y_after,
2362 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2364 y_before = y_after;
2366 r = SendMessage(hwndRichEdit, EM_SCROLL,
2367 SB_PAGEUP, 0);/*page up beyond top */
2369 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2371 ok(r == 0x00010000,
2372 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2373 ok(y_before == y_after,
2374 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2376 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2377 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2378 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2379 r = SendMessage(hwndRichEdit, EM_SCROLL,
2380 SB_PAGEDOWN, 0); /* page down beyond bot */
2381 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2383 ok(r == 0x00010000,
2384 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2385 ok(y_before == y_after,
2386 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2387 y_before, y_after);
2389 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2390 SendMessage(hwndRichEdit, EM_SCROLL,
2391 SB_LINEDOWN, 0); /* line down beyond bot */
2392 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2394 ok(r == 0x00010000,
2395 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2396 ok(y_before == y_after,
2397 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2398 y_before, y_after);
2400 DestroyWindow(hwndRichEdit);
2403 unsigned int recursionLevel = 0;
2404 unsigned int WM_SIZE_recursionLevel = 0;
2405 BOOL bailedOutOfRecursion = FALSE;
2406 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2408 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2410 LRESULT r;
2412 if (bailedOutOfRecursion) return 0;
2413 if (recursionLevel >= 32) {
2414 bailedOutOfRecursion = TRUE;
2415 return 0;
2418 recursionLevel++;
2419 switch (message) {
2420 case WM_SIZE:
2421 WM_SIZE_recursionLevel++;
2422 r = richeditProc(hwnd, message, wParam, lParam);
2423 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2424 ShowScrollBar(hwnd, SB_VERT, TRUE);
2425 WM_SIZE_recursionLevel--;
2426 break;
2427 default:
2428 r = richeditProc(hwnd, message, wParam, lParam);
2429 break;
2431 recursionLevel--;
2432 return r;
2435 static void test_scrollbar_visibility(void)
2437 HWND hwndRichEdit;
2438 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2439 SCROLLINFO si;
2440 WNDCLASSA cls;
2441 BOOL r;
2443 /* These tests show that richedit should temporarily refrain from automatically
2444 hiding or showing its scrollbars (vertical at least) when an explicit request
2445 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2446 Some applications depend on forced showing (when otherwise richedit would
2447 hide the vertical scrollbar) and are thrown on an endless recursive loop
2448 if richedit auto-hides the scrollbar again. Apparently they never heard of
2449 the ES_DISABLENOSCROLL style... */
2451 hwndRichEdit = new_richedit(NULL);
2453 /* Test default scrollbar visibility behavior */
2454 memset(&si, 0, sizeof(si));
2455 si.cbSize = sizeof(si);
2456 si.fMask = SIF_PAGE | SIF_RANGE;
2457 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2458 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2459 "Vertical scrollbar is visible, should be invisible.\n");
2460 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2461 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2462 si.nPage, si.nMin, si.nMax);
2464 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2465 memset(&si, 0, sizeof(si));
2466 si.cbSize = sizeof(si);
2467 si.fMask = SIF_PAGE | SIF_RANGE;
2468 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2469 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2470 "Vertical scrollbar is visible, should be invisible.\n");
2471 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2472 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2473 si.nPage, si.nMin, si.nMax);
2475 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2476 memset(&si, 0, sizeof(si));
2477 si.cbSize = sizeof(si);
2478 si.fMask = SIF_PAGE | SIF_RANGE;
2479 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2480 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2481 "Vertical scrollbar is invisible, should be visible.\n");
2482 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2483 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2484 si.nPage, si.nMin, si.nMax);
2486 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2487 even though it hides the scrollbar */
2488 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2489 memset(&si, 0, sizeof(si));
2490 si.cbSize = sizeof(si);
2491 si.fMask = SIF_PAGE | SIF_RANGE;
2492 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2493 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2494 "Vertical scrollbar is visible, should be invisible.\n");
2495 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2496 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2497 si.nPage, si.nMin, si.nMax);
2499 /* Setting non-scrolling text again does *not* reset scrollbar range */
2500 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2501 memset(&si, 0, sizeof(si));
2502 si.cbSize = sizeof(si);
2503 si.fMask = SIF_PAGE | SIF_RANGE;
2504 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2505 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2506 "Vertical scrollbar is visible, should be invisible.\n");
2507 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2508 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2509 si.nPage, si.nMin, si.nMax);
2511 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2512 memset(&si, 0, sizeof(si));
2513 si.cbSize = sizeof(si);
2514 si.fMask = SIF_PAGE | SIF_RANGE;
2515 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2516 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2517 "Vertical scrollbar is visible, should be invisible.\n");
2518 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2519 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2520 si.nPage, si.nMin, si.nMax);
2522 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2523 memset(&si, 0, sizeof(si));
2524 si.cbSize = sizeof(si);
2525 si.fMask = SIF_PAGE | SIF_RANGE;
2526 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2527 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2528 "Vertical scrollbar is visible, should be invisible.\n");
2529 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2530 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2531 si.nPage, si.nMin, si.nMax);
2533 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2534 memset(&si, 0, sizeof(si));
2535 si.cbSize = sizeof(si);
2536 si.fMask = SIF_PAGE | SIF_RANGE;
2537 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2538 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2539 "Vertical scrollbar is visible, should be invisible.\n");
2540 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2541 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2542 si.nPage, si.nMin, si.nMax);
2544 DestroyWindow(hwndRichEdit);
2546 /* Test again, with ES_DISABLENOSCROLL style */
2547 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2549 /* Test default scrollbar visibility behavior */
2550 memset(&si, 0, sizeof(si));
2551 si.cbSize = sizeof(si);
2552 si.fMask = SIF_PAGE | SIF_RANGE;
2553 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2554 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2555 "Vertical scrollbar is invisible, should be visible.\n");
2556 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2557 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2558 si.nPage, si.nMin, si.nMax);
2560 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2561 memset(&si, 0, sizeof(si));
2562 si.cbSize = sizeof(si);
2563 si.fMask = SIF_PAGE | SIF_RANGE;
2564 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2565 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2566 "Vertical scrollbar is invisible, should be visible.\n");
2567 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2568 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2569 si.nPage, si.nMin, si.nMax);
2571 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2572 memset(&si, 0, sizeof(si));
2573 si.cbSize = sizeof(si);
2574 si.fMask = SIF_PAGE | SIF_RANGE;
2575 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2576 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2577 "Vertical scrollbar is invisible, should be visible.\n");
2578 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2579 "reported page/range is %d (%d..%d)\n",
2580 si.nPage, si.nMin, si.nMax);
2582 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2583 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2584 memset(&si, 0, sizeof(si));
2585 si.cbSize = sizeof(si);
2586 si.fMask = SIF_PAGE | SIF_RANGE;
2587 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2588 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2589 "Vertical scrollbar is invisible, should be visible.\n");
2590 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2591 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2592 si.nPage, si.nMin, si.nMax);
2594 /* Setting non-scrolling text again does *not* reset scrollbar range */
2595 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2596 memset(&si, 0, sizeof(si));
2597 si.cbSize = sizeof(si);
2598 si.fMask = SIF_PAGE | SIF_RANGE;
2599 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2600 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2601 "Vertical scrollbar is invisible, should be visible.\n");
2602 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2603 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2604 si.nPage, si.nMin, si.nMax);
2606 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2607 memset(&si, 0, sizeof(si));
2608 si.cbSize = sizeof(si);
2609 si.fMask = SIF_PAGE | SIF_RANGE;
2610 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2611 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2612 "Vertical scrollbar is invisible, should be visible.\n");
2613 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2614 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2615 si.nPage, si.nMin, si.nMax);
2617 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2618 memset(&si, 0, sizeof(si));
2619 si.cbSize = sizeof(si);
2620 si.fMask = SIF_PAGE | SIF_RANGE;
2621 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2622 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2623 "Vertical scrollbar is invisible, should be visible.\n");
2624 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2625 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2626 si.nPage, si.nMin, si.nMax);
2628 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2629 memset(&si, 0, sizeof(si));
2630 si.cbSize = sizeof(si);
2631 si.fMask = SIF_PAGE | SIF_RANGE;
2632 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2633 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2634 "Vertical scrollbar is invisible, should be visible.\n");
2635 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2636 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2637 si.nPage, si.nMin, si.nMax);
2639 DestroyWindow(hwndRichEdit);
2641 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2642 hwndRichEdit = new_richedit(NULL);
2644 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2645 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2646 memset(&si, 0, sizeof(si));
2647 si.cbSize = sizeof(si);
2648 si.fMask = SIF_PAGE | SIF_RANGE;
2649 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2650 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2651 "Vertical scrollbar is invisible, should be visible.\n");
2652 todo_wine {
2653 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2654 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2655 si.nPage, si.nMin, si.nMax);
2658 /* Ditto, see above */
2659 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2660 memset(&si, 0, sizeof(si));
2661 si.cbSize = sizeof(si);
2662 si.fMask = SIF_PAGE | SIF_RANGE;
2663 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2664 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2665 "Vertical scrollbar is invisible, should be visible.\n");
2666 todo_wine {
2667 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2668 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2669 si.nPage, si.nMin, si.nMax);
2672 /* Ditto, see above */
2673 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2674 memset(&si, 0, sizeof(si));
2675 si.cbSize = sizeof(si);
2676 si.fMask = SIF_PAGE | SIF_RANGE;
2677 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2678 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2679 "Vertical scrollbar is invisible, should be visible.\n");
2680 todo_wine {
2681 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2682 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2683 si.nPage, si.nMin, si.nMax);
2686 /* Ditto, see above */
2687 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2688 memset(&si, 0, sizeof(si));
2689 si.cbSize = sizeof(si);
2690 si.fMask = SIF_PAGE | SIF_RANGE;
2691 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2692 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2693 "Vertical scrollbar is invisible, should be visible.\n");
2694 todo_wine {
2695 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2696 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2697 si.nPage, si.nMin, si.nMax);
2700 /* Ditto, see above */
2701 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2702 memset(&si, 0, sizeof(si));
2703 si.cbSize = sizeof(si);
2704 si.fMask = SIF_PAGE | SIF_RANGE;
2705 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2706 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2707 "Vertical scrollbar is invisible, should be visible.\n");
2708 todo_wine {
2709 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2710 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2711 si.nPage, si.nMin, si.nMax);
2714 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2715 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2716 memset(&si, 0, sizeof(si));
2717 si.cbSize = sizeof(si);
2718 si.fMask = SIF_PAGE | SIF_RANGE;
2719 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2720 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2721 "Vertical scrollbar is visible, should be invisible.\n");
2722 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2723 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2724 si.nPage, si.nMin, si.nMax);
2726 DestroyWindow(hwndRichEdit);
2728 hwndRichEdit = new_richedit(NULL);
2730 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2731 memset(&si, 0, sizeof(si));
2732 si.cbSize = sizeof(si);
2733 si.fMask = SIF_PAGE | SIF_RANGE;
2734 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2735 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2736 "Vertical scrollbar is visible, should be invisible.\n");
2737 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2738 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2739 si.nPage, si.nMin, si.nMax);
2741 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2742 memset(&si, 0, sizeof(si));
2743 si.cbSize = sizeof(si);
2744 si.fMask = SIF_PAGE | SIF_RANGE;
2745 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2746 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2747 "Vertical scrollbar is visible, should be invisible.\n");
2748 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2749 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2750 si.nPage, si.nMin, si.nMax);
2752 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2753 memset(&si, 0, sizeof(si));
2754 si.cbSize = sizeof(si);
2755 si.fMask = SIF_PAGE | SIF_RANGE;
2756 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2757 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2758 "Vertical scrollbar is visible, should be invisible.\n");
2759 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2760 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2761 si.nPage, si.nMin, si.nMax);
2763 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2764 memset(&si, 0, sizeof(si));
2765 si.cbSize = sizeof(si);
2766 si.fMask = SIF_PAGE | SIF_RANGE;
2767 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2768 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2769 "Vertical scrollbar is visible, should be invisible.\n");
2770 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2771 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2772 si.nPage, si.nMin, si.nMax);
2774 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2775 memset(&si, 0, sizeof(si));
2776 si.cbSize = sizeof(si);
2777 si.fMask = SIF_PAGE | SIF_RANGE;
2778 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2779 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2780 "Vertical scrollbar is invisible, should be visible.\n");
2781 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2782 "reported page/range is %d (%d..%d)\n",
2783 si.nPage, si.nMin, si.nMax);
2785 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2786 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2787 memset(&si, 0, sizeof(si));
2788 si.cbSize = sizeof(si);
2789 si.fMask = SIF_PAGE | SIF_RANGE;
2790 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2791 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2792 "Vertical scrollbar is visible, should be invisible.\n");
2793 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2794 "reported page/range is %d (%d..%d)\n",
2795 si.nPage, si.nMin, si.nMax);
2797 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2798 memset(&si, 0, sizeof(si));
2799 si.cbSize = sizeof(si);
2800 si.fMask = SIF_PAGE | SIF_RANGE;
2801 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2802 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2803 "Vertical scrollbar is visible, should be invisible.\n");
2804 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2805 "reported page/range is %d (%d..%d)\n",
2806 si.nPage, si.nMin, si.nMax);
2808 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2809 EM_SCROLL will make visible any forcefully invisible scrollbar */
2810 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2811 memset(&si, 0, sizeof(si));
2812 si.cbSize = sizeof(si);
2813 si.fMask = SIF_PAGE | SIF_RANGE;
2814 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2815 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2816 "Vertical scrollbar is invisible, should be visible.\n");
2817 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2818 "reported page/range is %d (%d..%d)\n",
2819 si.nPage, si.nMin, si.nMax);
2821 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2822 memset(&si, 0, sizeof(si));
2823 si.cbSize = sizeof(si);
2824 si.fMask = SIF_PAGE | SIF_RANGE;
2825 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2826 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2827 "Vertical scrollbar is visible, should be invisible.\n");
2828 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2829 "reported page/range is %d (%d..%d)\n",
2830 si.nPage, si.nMin, si.nMax);
2832 /* Again, EM_SCROLL, with SB_LINEUP */
2833 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2834 memset(&si, 0, sizeof(si));
2835 si.cbSize = sizeof(si);
2836 si.fMask = SIF_PAGE | SIF_RANGE;
2837 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2838 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2839 "Vertical scrollbar is invisible, should be visible.\n");
2840 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2841 "reported page/range is %d (%d..%d)\n",
2842 si.nPage, si.nMin, si.nMax);
2844 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2845 memset(&si, 0, sizeof(si));
2846 si.cbSize = sizeof(si);
2847 si.fMask = SIF_PAGE | SIF_RANGE;
2848 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2849 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2850 "Vertical scrollbar is visible, should be invisible.\n");
2851 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2852 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2853 si.nPage, si.nMin, si.nMax);
2855 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2856 memset(&si, 0, sizeof(si));
2857 si.cbSize = sizeof(si);
2858 si.fMask = SIF_PAGE | SIF_RANGE;
2859 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2860 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2861 "Vertical scrollbar is invisible, should be visible.\n");
2862 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2863 "reported page/range is %d (%d..%d)\n",
2864 si.nPage, si.nMin, si.nMax);
2866 DestroyWindow(hwndRichEdit);
2869 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2870 hwndRichEdit = new_richedit(NULL);
2872 #define ENABLE_WS_VSCROLL(hwnd) \
2873 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2874 #define DISABLE_WS_VSCROLL(hwnd) \
2875 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2877 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2878 ENABLE_WS_VSCROLL(hwndRichEdit);
2879 memset(&si, 0, sizeof(si));
2880 si.cbSize = sizeof(si);
2881 si.fMask = SIF_PAGE | SIF_RANGE;
2882 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2883 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2884 "Vertical scrollbar is invisible, should be visible.\n");
2885 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2886 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2887 si.nPage, si.nMin, si.nMax);
2889 /* Ditto, see above */
2890 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2891 memset(&si, 0, sizeof(si));
2892 si.cbSize = sizeof(si);
2893 si.fMask = SIF_PAGE | SIF_RANGE;
2894 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2895 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2896 "Vertical scrollbar is invisible, should be visible.\n");
2897 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2898 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2899 si.nPage, si.nMin, si.nMax);
2901 /* Ditto, see above */
2902 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2903 memset(&si, 0, sizeof(si));
2904 si.cbSize = sizeof(si);
2905 si.fMask = SIF_PAGE | SIF_RANGE;
2906 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2907 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2908 "Vertical scrollbar is invisible, should be visible.\n");
2909 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2910 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2911 si.nPage, si.nMin, si.nMax);
2913 /* Ditto, see above */
2914 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2915 memset(&si, 0, sizeof(si));
2916 si.cbSize = sizeof(si);
2917 si.fMask = SIF_PAGE | SIF_RANGE;
2918 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2919 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2920 "Vertical scrollbar is invisible, should be visible.\n");
2921 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2922 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2923 si.nPage, si.nMin, si.nMax);
2925 /* Ditto, see above */
2926 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2927 memset(&si, 0, sizeof(si));
2928 si.cbSize = sizeof(si);
2929 si.fMask = SIF_PAGE | SIF_RANGE;
2930 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2931 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2932 "Vertical scrollbar is invisible, should be visible.\n");
2933 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2934 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2935 si.nPage, si.nMin, si.nMax);
2937 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2938 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2939 memset(&si, 0, sizeof(si));
2940 si.cbSize = sizeof(si);
2941 si.fMask = SIF_PAGE | SIF_RANGE;
2942 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2943 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2944 "Vertical scrollbar is visible, should be invisible.\n");
2945 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2946 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2947 si.nPage, si.nMin, si.nMax);
2949 DestroyWindow(hwndRichEdit);
2951 hwndRichEdit = new_richedit(NULL);
2953 DISABLE_WS_VSCROLL(hwndRichEdit);
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 visible, should be invisible.\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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2965 memset(&si, 0, sizeof(si));
2966 si.cbSize = sizeof(si);
2967 si.fMask = SIF_PAGE | SIF_RANGE;
2968 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2969 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2970 "Vertical scrollbar is visible, should be invisible.\n");
2971 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2972 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2973 si.nPage, si.nMin, si.nMax);
2975 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2976 memset(&si, 0, sizeof(si));
2977 si.cbSize = sizeof(si);
2978 si.fMask = SIF_PAGE | SIF_RANGE;
2979 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2980 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2981 "Vertical scrollbar is visible, should be invisible.\n");
2982 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2983 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2984 si.nPage, si.nMin, si.nMax);
2986 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2987 memset(&si, 0, sizeof(si));
2988 si.cbSize = sizeof(si);
2989 si.fMask = SIF_PAGE | SIF_RANGE;
2990 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2991 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2992 "Vertical scrollbar is visible, should be invisible.\n");
2993 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2994 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2995 si.nPage, si.nMin, si.nMax);
2997 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2998 memset(&si, 0, sizeof(si));
2999 si.cbSize = sizeof(si);
3000 si.fMask = SIF_PAGE | SIF_RANGE;
3001 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3002 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3003 "Vertical scrollbar is invisible, should be visible.\n");
3004 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3005 "reported page/range is %d (%d..%d)\n",
3006 si.nPage, si.nMin, si.nMax);
3008 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3009 DISABLE_WS_VSCROLL(hwndRichEdit);
3010 memset(&si, 0, sizeof(si));
3011 si.cbSize = sizeof(si);
3012 si.fMask = SIF_PAGE | SIF_RANGE;
3013 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3014 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3015 "Vertical scrollbar is visible, should be invisible.\n");
3016 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3017 "reported page/range is %d (%d..%d)\n",
3018 si.nPage, si.nMin, si.nMax);
3020 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3021 memset(&si, 0, sizeof(si));
3022 si.cbSize = sizeof(si);
3023 si.fMask = SIF_PAGE | SIF_RANGE;
3024 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3025 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3026 "Vertical scrollbar is visible, should be invisible.\n");
3027 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3028 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3029 si.nPage, si.nMin, si.nMax);
3031 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3032 memset(&si, 0, sizeof(si));
3033 si.cbSize = sizeof(si);
3034 si.fMask = SIF_PAGE | SIF_RANGE;
3035 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3036 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3037 "Vertical scrollbar is invisible, should be visible.\n");
3038 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3039 "reported page/range is %d (%d..%d)\n",
3040 si.nPage, si.nMin, si.nMax);
3042 DISABLE_WS_VSCROLL(hwndRichEdit);
3043 memset(&si, 0, sizeof(si));
3044 si.cbSize = sizeof(si);
3045 si.fMask = SIF_PAGE | SIF_RANGE;
3046 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3047 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3048 "Vertical scrollbar is visible, should be invisible.\n");
3049 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3050 "reported page/range is %d (%d..%d)\n",
3051 si.nPage, si.nMin, si.nMax);
3053 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3054 EM_SCROLL will make visible any forcefully invisible scrollbar */
3055 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3056 memset(&si, 0, sizeof(si));
3057 si.cbSize = sizeof(si);
3058 si.fMask = SIF_PAGE | SIF_RANGE;
3059 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3060 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3061 "Vertical scrollbar is invisible, should be visible.\n");
3062 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3063 "reported page/range is %d (%d..%d)\n",
3064 si.nPage, si.nMin, si.nMax);
3066 DISABLE_WS_VSCROLL(hwndRichEdit);
3067 memset(&si, 0, sizeof(si));
3068 si.cbSize = sizeof(si);
3069 si.fMask = SIF_PAGE | SIF_RANGE;
3070 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3071 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3072 "Vertical scrollbar is visible, should be invisible.\n");
3073 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3074 "reported page/range is %d (%d..%d)\n",
3075 si.nPage, si.nMin, si.nMax);
3077 /* Again, EM_SCROLL, with SB_LINEUP */
3078 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3079 memset(&si, 0, sizeof(si));
3080 si.cbSize = sizeof(si);
3081 si.fMask = SIF_PAGE | SIF_RANGE;
3082 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3083 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3084 "Vertical scrollbar is invisible, should be visible.\n");
3085 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3086 "reported page/range is %d (%d..%d)\n",
3087 si.nPage, si.nMin, si.nMax);
3089 DestroyWindow(hwndRichEdit);
3091 /* This window proc models what is going on with Corman Lisp 3.0.
3092 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3093 force the scrollbar into visibility. Recursion should NOT happen
3094 as a result of this action.
3096 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3097 if (r) {
3098 richeditProc = cls.lpfnWndProc;
3099 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3100 cls.lpszClassName = "RicheditStupidOverride";
3101 if(!RegisterClassA(&cls)) assert(0);
3103 recursionLevel = 0;
3104 WM_SIZE_recursionLevel = 0;
3105 bailedOutOfRecursion = FALSE;
3106 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3107 ok(!bailedOutOfRecursion,
3108 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3110 recursionLevel = 0;
3111 WM_SIZE_recursionLevel = 0;
3112 bailedOutOfRecursion = FALSE;
3113 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3114 ok(!bailedOutOfRecursion,
3115 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3117 /* Unblock window in order to process WM_DESTROY */
3118 recursionLevel = 0;
3119 bailedOutOfRecursion = FALSE;
3120 WM_SIZE_recursionLevel = 0;
3121 DestroyWindow(hwndRichEdit);
3125 static void test_EM_SETUNDOLIMIT(void)
3127 /* cases we test for:
3128 * default behaviour - limiting at 100 undo's
3129 * undo disabled - setting a limit of 0
3130 * undo limited - undo limit set to some to some number, like 2
3131 * bad input - sending a negative number should default to 100 undo's */
3133 HWND hwndRichEdit = new_richedit(NULL);
3134 CHARRANGE cr;
3135 int i;
3136 int result;
3138 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3139 cr.cpMin = 0;
3140 cr.cpMax = 1;
3141 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3142 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3143 also, multiple pastes don't combine like WM_CHAR would */
3144 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3146 /* first case - check the default */
3147 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3148 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3149 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3150 for (i=0; i<100; i++) /* Undo 100 of them */
3151 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3152 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3153 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3155 /* second case - cannot undo */
3156 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3157 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3158 SendMessage(hwndRichEdit,
3159 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3160 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3161 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3163 /* third case - set it to an arbitrary number */
3164 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3165 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3166 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3167 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3168 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3169 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3170 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3171 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3172 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3173 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3174 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3175 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3176 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3177 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3179 /* fourth case - setting negative numbers should default to 100 undos */
3180 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3181 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3182 ok (result == 100,
3183 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3185 DestroyWindow(hwndRichEdit);
3188 static void test_ES_PASSWORD(void)
3190 /* This isn't hugely testable, so we're just going to run it through its paces */
3192 HWND hwndRichEdit = new_richedit(NULL);
3193 WCHAR result;
3195 /* First, check the default of a regular control */
3196 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3197 ok (result == 0,
3198 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3200 /* Now, set it to something normal */
3201 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3202 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3203 ok (result == 120,
3204 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3206 /* Now, set it to something odd */
3207 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3208 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3209 ok (result == 1234,
3210 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3211 DestroyWindow(hwndRichEdit);
3214 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3215 LPBYTE pbBuff,
3216 LONG cb,
3217 LONG *pcb)
3219 char** str = (char**)dwCookie;
3220 *pcb = cb;
3221 if (*pcb > 0) {
3222 memcpy(*str, pbBuff, *pcb);
3223 *str += *pcb;
3225 return 0;
3228 static void test_WM_SETTEXT(void)
3230 HWND hwndRichEdit = new_richedit(NULL);
3231 const char * TestItem1 = "TestSomeText";
3232 const char * TestItem2 = "TestSomeText\r";
3233 const char * TestItem2_after = "TestSomeText\r\n";
3234 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3235 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3236 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3237 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3238 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3239 const char * TestItem5_after = "TestSomeText TestSomeText";
3240 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3241 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3242 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3243 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3245 char buf[1024] = {0};
3246 LRESULT result;
3247 EDITSTREAM es;
3248 char * p;
3250 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3251 any solitary \r to be converted to \r\n on return. Properly paired
3252 \r\n are not affected. It also shows that the special sequence \r\r\n
3253 gets converted to a single space.
3256 #define TEST_SETTEXT(a, b) \
3257 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3258 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3259 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3260 ok (result == lstrlen(buf), \
3261 "WM_GETTEXT returned %ld instead of expected %u\n", \
3262 result, lstrlen(buf)); \
3263 result = strcmp(b, buf); \
3264 ok(result == 0, \
3265 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3267 TEST_SETTEXT(TestItem1, TestItem1)
3268 TEST_SETTEXT(TestItem2, TestItem2_after)
3269 TEST_SETTEXT(TestItem3, TestItem3_after)
3270 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3271 TEST_SETTEXT(TestItem4, TestItem4_after)
3272 TEST_SETTEXT(TestItem5, TestItem5_after)
3273 TEST_SETTEXT(TestItem6, TestItem6_after)
3274 TEST_SETTEXT(TestItem7, TestItem7_after)
3276 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3277 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3278 p = buf;
3279 es.dwCookie = (DWORD_PTR)&p;
3280 es.dwError = 0;
3281 es.pfnCallback = test_WM_SETTEXT_esCallback;
3282 memset(buf, 0, sizeof(buf));
3283 SendMessage(hwndRichEdit, EM_STREAMOUT,
3284 (WPARAM)(SF_RTF), (LPARAM)&es);
3285 trace("EM_STREAMOUT produced: \n%s\n", buf);
3286 TEST_SETTEXT(buf, TestItem1)
3288 #undef TEST_SETTEXT
3289 DestroyWindow(hwndRichEdit);
3292 static void test_EM_STREAMOUT(void)
3294 HWND hwndRichEdit = new_richedit(NULL);
3295 int r;
3296 EDITSTREAM es;
3297 char buf[1024] = {0};
3298 char * p;
3300 const char * TestItem1 = "TestSomeText";
3301 const char * TestItem2 = "TestSomeText\r";
3302 const char * TestItem3 = "TestSomeText\r\n";
3304 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3305 p = buf;
3306 es.dwCookie = (DWORD_PTR)&p;
3307 es.dwError = 0;
3308 es.pfnCallback = test_WM_SETTEXT_esCallback;
3309 memset(buf, 0, sizeof(buf));
3310 SendMessage(hwndRichEdit, EM_STREAMOUT,
3311 (WPARAM)(SF_TEXT), (LPARAM)&es);
3312 r = strlen(buf);
3313 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3314 ok(strcmp(buf, TestItem1) == 0,
3315 "streamed text different, got %s\n", buf);
3317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3318 p = buf;
3319 es.dwCookie = (DWORD_PTR)&p;
3320 es.dwError = 0;
3321 es.pfnCallback = test_WM_SETTEXT_esCallback;
3322 memset(buf, 0, sizeof(buf));
3323 SendMessage(hwndRichEdit, EM_STREAMOUT,
3324 (WPARAM)(SF_TEXT), (LPARAM)&es);
3325 r = strlen(buf);
3326 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3327 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3328 ok(strcmp(buf, TestItem3) == 0,
3329 "streamed text different from, got %s\n", buf);
3330 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3331 p = buf;
3332 es.dwCookie = (DWORD_PTR)&p;
3333 es.dwError = 0;
3334 es.pfnCallback = test_WM_SETTEXT_esCallback;
3335 memset(buf, 0, sizeof(buf));
3336 SendMessage(hwndRichEdit, EM_STREAMOUT,
3337 (WPARAM)(SF_TEXT), (LPARAM)&es);
3338 r = strlen(buf);
3339 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3340 ok(strcmp(buf, TestItem3) == 0,
3341 "streamed text different, got %s\n", buf);
3343 DestroyWindow(hwndRichEdit);
3346 static void test_EM_SETTEXTEX(void)
3348 HWND hwndRichEdit, parent;
3349 SCROLLINFO si;
3350 int sel_start, sel_end;
3351 SETTEXTEX setText;
3352 GETTEXTEX getText;
3353 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3354 'S', 'o', 'm', 'e',
3355 'T', 'e', 'x', 't', 0};
3356 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3357 't', 'S', 'o', 'm',
3358 'e', 'T', 'e', 'x',
3359 't', 't', 'S', 'o',
3360 'm', 'e', 'T', 'e',
3361 'x', 't', 0};
3362 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3363 '\r','t','S','o','m','e','T','e','x','t',0};
3364 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3365 'S', 'o', 'm', 'e',
3366 'T', 'e', 'x', 't',
3367 '\r', 0};
3368 const char * TestItem2_after = "TestSomeText\r\n";
3369 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3370 'S', 'o', 'm', 'e',
3371 'T', 'e', 'x', 't',
3372 '\r','\n','\r','\n', 0};
3373 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3374 'S', 'o', 'm', 'e',
3375 'T', 'e', 'x', 't',
3376 '\n','\n', 0};
3377 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3378 'S', 'o', 'm', 'e',
3379 'T', 'e', 'x', 't',
3380 '\r','\r', 0};
3381 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3382 'S', 'o', 'm', 'e',
3383 'T', 'e', 'x', 't',
3384 '\r','\r','\n','\r',
3385 '\n', 0};
3386 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3387 'S', 'o', 'm', 'e',
3388 'T', 'e', 'x', 't',
3389 ' ','\r', 0};
3390 #define MAX_BUF_LEN 1024
3391 WCHAR buf[MAX_BUF_LEN];
3392 char bufACP[MAX_BUF_LEN];
3393 char * p;
3394 int result;
3395 CHARRANGE cr;
3396 EDITSTREAM es;
3397 WNDCLASSA cls;
3399 /* Test the scroll position with and without a parent window.
3401 * For some reason the scroll position is 0 after EM_SETTEXTEX
3402 * with the ST_SELECTION flag only when the control has a parent
3403 * window, even though the selection is at the end. */
3404 cls.style = 0;
3405 cls.lpfnWndProc = DefWindowProcA;
3406 cls.cbClsExtra = 0;
3407 cls.cbWndExtra = 0;
3408 cls.hInstance = GetModuleHandleA(0);
3409 cls.hIcon = 0;
3410 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3411 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3412 cls.lpszMenuName = NULL;
3413 cls.lpszClassName = "ParentTestClass";
3414 if(!RegisterClassA(&cls)) assert(0);
3416 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3417 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3418 ok (parent != 0, "Failed to create parent window\n");
3420 hwndRichEdit = CreateWindowEx(0,
3421 RICHEDIT_CLASS, NULL,
3422 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3423 0, 0, 200, 60, parent, NULL,
3424 hmoduleRichEdit, NULL);
3426 setText.codepage = CP_ACP;
3427 setText.flags = ST_SELECTION;
3428 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3429 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3430 si.cbSize = sizeof(si);
3431 si.fMask = SIF_ALL;
3432 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3433 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3434 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3435 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3436 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3438 DestroyWindow(parent);
3440 /* Test without a parent window */
3441 hwndRichEdit = new_richedit(NULL);
3442 setText.codepage = CP_ACP;
3443 setText.flags = ST_SELECTION;
3444 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3445 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3446 si.cbSize = sizeof(si);
3447 si.fMask = SIF_ALL;
3448 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3449 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3450 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3451 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3452 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3454 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3455 * but this time it is because the selection is at the beginning. */
3456 setText.codepage = CP_ACP;
3457 setText.flags = ST_DEFAULT;
3458 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3459 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3460 si.cbSize = sizeof(si);
3461 si.fMask = SIF_ALL;
3462 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3463 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3464 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3465 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3466 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3468 setText.codepage = 1200; /* no constant for unicode */
3469 getText.codepage = 1200; /* no constant for unicode */
3470 getText.cb = MAX_BUF_LEN;
3471 getText.flags = GT_DEFAULT;
3472 getText.lpDefaultChar = NULL;
3473 getText.lpUsedDefChar = NULL;
3475 setText.flags = 0;
3476 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3477 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3478 ok(lstrcmpW(buf, TestItem1) == 0,
3479 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3481 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3482 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3484 setText.codepage = 1200; /* no constant for unicode */
3485 getText.codepage = 1200; /* no constant for unicode */
3486 getText.cb = MAX_BUF_LEN;
3487 getText.flags = GT_DEFAULT;
3488 getText.lpDefaultChar = NULL;
3489 getText.lpUsedDefChar = NULL;
3490 setText.flags = 0;
3491 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3492 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3493 ok(lstrcmpW(buf, TestItem2) == 0,
3494 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3496 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3497 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3498 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3499 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3501 /* Baseline test for just-enough buffer space for string */
3502 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3503 getText.codepage = 1200; /* no constant for unicode */
3504 getText.flags = GT_DEFAULT;
3505 getText.lpDefaultChar = NULL;
3506 getText.lpUsedDefChar = NULL;
3507 memset(buf, 0, MAX_BUF_LEN);
3508 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3509 ok(lstrcmpW(buf, TestItem2) == 0,
3510 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3512 /* When there is enough space for one character, but not both, of the CRLF
3513 pair at the end of the string, the CR is not copied at all. That is,
3514 the caller must not see CRLF pairs truncated to CR at the end of the
3515 string.
3517 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3518 getText.codepage = 1200; /* no constant for unicode */
3519 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3520 getText.lpDefaultChar = NULL;
3521 getText.lpUsedDefChar = NULL;
3522 memset(buf, 0, MAX_BUF_LEN);
3523 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3524 ok(lstrcmpW(buf, TestItem1) == 0,
3525 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3528 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3529 setText.codepage = 1200; /* no constant for unicode */
3530 getText.codepage = 1200; /* no constant for unicode */
3531 getText.cb = MAX_BUF_LEN;
3532 getText.flags = GT_DEFAULT;
3533 getText.lpDefaultChar = NULL;
3534 getText.lpUsedDefChar = NULL;
3535 setText.flags = 0;
3536 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3537 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3538 ok(lstrcmpW(buf, TestItem3_after) == 0,
3539 "EM_SETTEXTEX did not convert properly\n");
3541 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3542 setText.codepage = 1200; /* no constant for unicode */
3543 getText.codepage = 1200; /* no constant for unicode */
3544 getText.cb = MAX_BUF_LEN;
3545 getText.flags = GT_DEFAULT;
3546 getText.lpDefaultChar = NULL;
3547 getText.lpUsedDefChar = NULL;
3548 setText.flags = 0;
3549 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3550 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3551 ok(lstrcmpW(buf, TestItem3_after) == 0,
3552 "EM_SETTEXTEX did not convert properly\n");
3554 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3555 setText.codepage = 1200; /* no constant for unicode */
3556 getText.codepage = 1200; /* no constant for unicode */
3557 getText.cb = MAX_BUF_LEN;
3558 getText.flags = GT_DEFAULT;
3559 getText.lpDefaultChar = NULL;
3560 getText.lpUsedDefChar = NULL;
3561 setText.flags = 0;
3562 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3563 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3564 ok(lstrcmpW(buf, TestItem4_after) == 0,
3565 "EM_SETTEXTEX did not convert properly\n");
3567 /* !ST_SELECTION && Unicode && !\rtf */
3568 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3569 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3571 ok (result == 1,
3572 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3573 ok(lstrlenW(buf) == 0,
3574 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3576 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3577 setText.flags = 0;
3578 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3579 /* select some text */
3580 cr.cpMax = 1;
3581 cr.cpMin = 3;
3582 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3583 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3584 setText.flags = ST_SELECTION;
3585 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3586 ok(result == 0,
3587 "EM_SETTEXTEX with NULL lParam to replace selection"
3588 " with no text should return 0. Got %i\n",
3589 result);
3591 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3592 setText.flags = 0;
3593 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3594 /* select some text */
3595 cr.cpMax = 1;
3596 cr.cpMin = 3;
3597 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3598 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3599 setText.flags = ST_SELECTION;
3600 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3601 (WPARAM)&setText, (LPARAM) TestItem1);
3602 /* get text */
3603 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3604 ok(result == lstrlenW(TestItem1),
3605 "EM_SETTEXTEX with NULL lParam to replace selection"
3606 " with no text should return 0. Got %i\n",
3607 result);
3608 ok(lstrlenW(buf) == 22,
3609 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3610 lstrlenW(buf) );
3612 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3614 p = (char *)buf;
3615 es.dwCookie = (DWORD_PTR)&p;
3616 es.dwError = 0;
3617 es.pfnCallback = test_WM_SETTEXT_esCallback;
3618 memset(buf, 0, sizeof(buf));
3619 SendMessage(hwndRichEdit, EM_STREAMOUT,
3620 (WPARAM)(SF_RTF), (LPARAM)&es);
3621 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3623 /* !ST_SELECTION && !Unicode && \rtf */
3624 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3625 getText.codepage = 1200; /* no constant for unicode */
3626 getText.cb = MAX_BUF_LEN;
3627 getText.flags = GT_DEFAULT;
3628 getText.lpDefaultChar = NULL;
3629 getText.lpUsedDefChar = NULL;
3631 setText.flags = 0;
3632 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3633 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3634 ok(lstrcmpW(buf, TestItem1) == 0,
3635 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3637 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3638 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3639 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3640 getText.codepage = CP_ACP;
3641 getText.cb = MAX_BUF_LEN;
3642 getText.flags = GT_DEFAULT;
3643 getText.lpDefaultChar = NULL;
3644 getText.lpUsedDefChar = NULL;
3646 setText.flags = ST_SELECTION;
3647 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3648 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3649 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3650 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3651 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3653 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3654 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3655 p = (char *)buf;
3656 es.dwCookie = (DWORD_PTR)&p;
3657 es.dwError = 0;
3658 es.pfnCallback = test_WM_SETTEXT_esCallback;
3659 memset(buf, 0, sizeof(buf));
3660 SendMessage(hwndRichEdit, EM_STREAMOUT,
3661 (WPARAM)(SF_RTF), (LPARAM)&es);
3662 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3664 /* select some text */
3665 cr.cpMax = 1;
3666 cr.cpMin = 3;
3667 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3669 /* ST_SELECTION && !Unicode && \rtf */
3670 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3671 getText.codepage = 1200; /* no constant for unicode */
3672 getText.cb = MAX_BUF_LEN;
3673 getText.flags = GT_DEFAULT;
3674 getText.lpDefaultChar = NULL;
3675 getText.lpUsedDefChar = NULL;
3677 setText.flags = ST_SELECTION;
3678 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3679 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3680 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3682 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3683 setText.codepage = 1200; /* no constant for unicode */
3684 getText.codepage = CP_ACP;
3685 getText.cb = MAX_BUF_LEN;
3687 setText.flags = 0;
3688 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3689 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3691 /* select some text */
3692 cr.cpMax = 1;
3693 cr.cpMin = 3;
3694 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3696 /* ST_SELECTION && !Unicode && !\rtf */
3697 setText.codepage = CP_ACP;
3698 getText.codepage = 1200; /* no constant for unicode */
3699 getText.cb = MAX_BUF_LEN;
3700 getText.flags = GT_DEFAULT;
3701 getText.lpDefaultChar = NULL;
3702 getText.lpUsedDefChar = NULL;
3704 setText.flags = ST_SELECTION;
3705 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3706 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3707 ok(lstrcmpW(buf, TestItem1alt) == 0,
3708 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3709 " using ST_SELECTION and non-Unicode\n");
3711 /* Test setting text using rich text format */
3712 setText.flags = 0;
3713 setText.codepage = CP_ACP;
3714 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3715 getText.codepage = CP_ACP;
3716 getText.cb = MAX_BUF_LEN;
3717 getText.flags = GT_DEFAULT;
3718 getText.lpDefaultChar = NULL;
3719 getText.lpUsedDefChar = NULL;
3720 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3721 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3723 setText.flags = 0;
3724 setText.codepage = CP_ACP;
3725 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3726 getText.codepage = CP_ACP;
3727 getText.cb = MAX_BUF_LEN;
3728 getText.flags = GT_DEFAULT;
3729 getText.lpDefaultChar = NULL;
3730 getText.lpUsedDefChar = NULL;
3731 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3732 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3734 DestroyWindow(hwndRichEdit);
3737 static void test_EM_LIMITTEXT(void)
3739 int ret;
3741 HWND hwndRichEdit = new_richedit(NULL);
3743 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3744 * about setting the length to -1 for multiline edit controls doesn't happen.
3747 /* Don't check default gettextlimit case. That's done in other tests */
3749 /* Set textlimit to 100 */
3750 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3751 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3752 ok (ret == 100,
3753 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3755 /* Set textlimit to 0 */
3756 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3757 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3758 ok (ret == 65536,
3759 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3761 /* Set textlimit to -1 */
3762 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3763 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3764 ok (ret == -1,
3765 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3767 /* Set textlimit to -2 */
3768 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3769 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3770 ok (ret == -2,
3771 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3773 DestroyWindow (hwndRichEdit);
3777 static void test_EM_EXLIMITTEXT(void)
3779 int i, selBegin, selEnd, len1, len2;
3780 int result;
3781 char text[1024 + 1];
3782 char buffer[1024 + 1];
3783 int textlimit = 0; /* multiple of 100 */
3784 HWND hwndRichEdit = new_richedit(NULL);
3786 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3787 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3789 textlimit = 256000;
3790 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3791 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3792 /* set higher */
3793 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3795 textlimit = 1000;
3796 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3797 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3798 /* set lower */
3799 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3801 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3802 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3803 /* default for WParam = 0 */
3804 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3806 textlimit = sizeof(text)-1;
3807 memset(text, 'W', textlimit);
3808 text[sizeof(text)-1] = 0;
3809 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3810 /* maxed out text */
3811 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3813 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3814 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3815 len1 = selEnd - selBegin;
3817 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3818 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3819 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3820 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3821 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3822 len2 = selEnd - selBegin;
3824 ok(len1 != len2,
3825 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3826 len1,len2,i);
3828 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3829 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3830 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3831 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3832 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3833 len1 = selEnd - selBegin;
3835 ok(len1 != len2,
3836 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3837 len1,len2,i);
3839 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3840 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3841 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3842 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3843 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3844 len2 = selEnd - selBegin;
3846 ok(len1 == len2,
3847 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3848 len1,len2,i);
3850 /* set text up to the limit, select all the text, then add a char */
3851 textlimit = 5;
3852 memset(text, 'W', textlimit);
3853 text[textlimit] = 0;
3854 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3855 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3856 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3857 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3858 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3859 result = strcmp(buffer, "A");
3860 ok(0 == result, "got string = \"%s\"\n", buffer);
3862 /* WM_SETTEXT not limited */
3863 textlimit = 10;
3864 memset(text, 'W', textlimit);
3865 text[textlimit] = 0;
3866 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3867 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3868 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3869 i = strlen(buffer);
3870 ok(10 == i, "expected 10 chars\n");
3871 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3872 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3874 /* try inserting more text at end */
3875 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3876 ok(0 == i, "WM_CHAR wasn't processed\n");
3877 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3878 i = strlen(buffer);
3879 ok(10 == i, "expected 10 chars, got %i\n", i);
3880 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3881 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3883 /* try inserting text at beginning */
3884 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3885 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3886 ok(0 == i, "WM_CHAR wasn't processed\n");
3887 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3888 i = strlen(buffer);
3889 ok(10 == i, "expected 10 chars, got %i\n", i);
3890 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3891 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3893 /* WM_CHAR is limited */
3894 textlimit = 1;
3895 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3896 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3897 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3898 ok(0 == i, "WM_CHAR wasn't processed\n");
3899 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3900 ok(0 == i, "WM_CHAR wasn't processed\n");
3901 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3902 i = strlen(buffer);
3903 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3905 DestroyWindow(hwndRichEdit);
3908 static void test_EM_GETLIMITTEXT(void)
3910 int i;
3911 HWND hwndRichEdit = new_richedit(NULL);
3913 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3914 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3916 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3917 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3918 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3920 DestroyWindow(hwndRichEdit);
3923 static void test_WM_SETFONT(void)
3925 /* There is no invalid input or error conditions for this function.
3926 * NULL wParam and lParam just fall back to their default values
3927 * It should be noted that even if you use a gibberish name for your fonts
3928 * here, it will still work because the name is stored. They will display as
3929 * System, but will report their name to be whatever they were created as */
3931 HWND hwndRichEdit = new_richedit(NULL);
3932 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3933 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3934 FF_DONTCARE, "Marlett");
3935 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3936 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3937 FF_DONTCARE, "MS Sans Serif");
3938 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3939 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3940 FF_DONTCARE, "Courier");
3941 LOGFONTA sentLogFont;
3942 CHARFORMAT2A returnedCF2A;
3944 returnedCF2A.cbSize = sizeof(returnedCF2A);
3946 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3947 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
3948 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3950 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3951 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3952 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3953 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3955 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
3956 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3957 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3958 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3959 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3960 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3962 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
3963 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3964 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3965 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3966 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3967 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3969 /* This last test is special since we send in NULL. We clear the variables
3970 * and just compare to "System" instead of the sent in font name. */
3971 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3972 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3973 returnedCF2A.cbSize = sizeof(returnedCF2A);
3975 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
3976 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3977 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3978 ok (!strcmp("System",returnedCF2A.szFaceName),
3979 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3981 DestroyWindow(hwndRichEdit);
3985 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3986 LPBYTE pbBuff,
3987 LONG cb,
3988 LONG *pcb)
3990 const char** str = (const char**)dwCookie;
3991 int size = strlen(*str);
3992 if(size > 3) /* let's make it piecemeal for fun */
3993 size = 3;
3994 *pcb = cb;
3995 if (*pcb > size) {
3996 *pcb = size;
3998 if (*pcb > 0) {
3999 memcpy(pbBuff, *str, *pcb);
4000 *str += *pcb;
4002 return 0;
4005 static void test_EM_GETMODIFY(void)
4007 HWND hwndRichEdit = new_richedit(NULL);
4008 LRESULT result;
4009 SETTEXTEX setText;
4010 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4011 'S', 'o', 'm', 'e',
4012 'T', 'e', 'x', 't', 0};
4013 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4014 'S', 'o', 'm', 'e',
4015 'O', 't', 'h', 'e', 'r',
4016 'T', 'e', 'x', 't', 0};
4017 const char* streamText = "hello world";
4018 CHARFORMAT2 cf2;
4019 PARAFORMAT2 pf2;
4020 EDITSTREAM es;
4022 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4023 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4024 FF_DONTCARE, "Courier");
4026 setText.codepage = 1200; /* no constant for unicode */
4027 setText.flags = ST_KEEPUNDO;
4030 /* modify flag shouldn't be set when richedit is first created */
4031 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4032 ok (result == 0,
4033 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4035 /* setting modify flag should actually set it */
4036 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4037 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4038 ok (result != 0,
4039 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4041 /* clearing modify flag should actually clear it */
4042 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4043 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4044 ok (result == 0,
4045 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4047 /* setting font doesn't change modify flag */
4048 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4049 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4050 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4051 ok (result == 0,
4052 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4054 /* setting text should set modify flag */
4055 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4056 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4057 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4058 ok (result != 0,
4059 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4061 /* undo previous text doesn't reset modify flag */
4062 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4063 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4064 ok (result != 0,
4065 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4067 /* set text with no flag to keep undo stack should not set modify flag */
4068 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4069 setText.flags = 0;
4070 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4071 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4072 ok (result == 0,
4073 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4075 /* WM_SETTEXT doesn't modify */
4076 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4077 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4078 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4079 ok (result == 0,
4080 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4082 /* clear the text */
4083 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4084 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4085 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4086 ok (result == 0,
4087 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4089 /* replace text */
4090 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4091 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4092 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4093 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4094 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4095 ok (result != 0,
4096 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4098 /* copy/paste text 1 */
4099 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4100 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4101 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4102 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4103 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4104 ok (result != 0,
4105 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4107 /* copy/paste text 2 */
4108 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4109 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4110 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4111 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4112 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4113 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4114 ok (result != 0,
4115 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4117 /* press char */
4118 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4119 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4120 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4121 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4122 ok (result != 0,
4123 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4125 /* press del */
4126 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4127 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4128 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4129 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4130 ok (result != 0,
4131 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4133 /* set char format */
4134 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4135 cf2.cbSize = sizeof(CHARFORMAT2);
4136 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4137 (LPARAM) &cf2);
4138 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4139 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4140 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4141 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4142 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4143 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4144 ok (result != 0,
4145 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4147 /* set para format */
4148 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4149 pf2.cbSize = sizeof(PARAFORMAT2);
4150 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4151 (LPARAM) &pf2);
4152 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4153 pf2.wAlignment = PFA_RIGHT;
4154 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4155 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4156 ok (result == 0,
4157 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4159 /* EM_STREAM */
4160 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4161 es.dwCookie = (DWORD_PTR)&streamText;
4162 es.dwError = 0;
4163 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4164 SendMessage(hwndRichEdit, EM_STREAMIN,
4165 (WPARAM)(SF_TEXT), (LPARAM)&es);
4166 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4167 ok (result != 0,
4168 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4170 DestroyWindow(hwndRichEdit);
4173 struct exsetsel_s {
4174 long min;
4175 long max;
4176 long expected_retval;
4177 int expected_getsel_start;
4178 int expected_getsel_end;
4179 int _exsetsel_todo_wine;
4180 int _getsel_todo_wine;
4183 const struct exsetsel_s exsetsel_tests[] = {
4184 /* sanity tests */
4185 {5, 10, 10, 5, 10, 0, 0},
4186 {15, 17, 17, 15, 17, 0, 0},
4187 /* test cpMax > strlen() */
4188 {0, 100, 18, 0, 18, 0, 1},
4189 /* test cpMin == cpMax */
4190 {5, 5, 5, 5, 5, 0, 0},
4191 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4192 {-1, 0, 5, 5, 5, 0, 0},
4193 {-1, 17, 5, 5, 5, 0, 0},
4194 {-1, 18, 5, 5, 5, 0, 0},
4195 /* test cpMin < 0 && cpMax < 0 */
4196 {-1, -1, 17, 17, 17, 0, 0},
4197 {-4, -5, 17, 17, 17, 0, 0},
4198 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4199 {0, -1, 18, 0, 18, 0, 1},
4200 {17, -5, 18, 17, 18, 0, 1},
4201 {18, -3, 17, 17, 17, 0, 0},
4202 /* test if cpMin > cpMax */
4203 {15, 19, 18, 15, 18, 0, 1},
4204 {19, 15, 18, 15, 18, 0, 1}
4207 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4208 CHARRANGE cr;
4209 long result;
4210 int start, end;
4212 cr.cpMin = setsel->min;
4213 cr.cpMax = setsel->max;
4214 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4216 if (setsel->_exsetsel_todo_wine) {
4217 todo_wine {
4218 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4220 } else {
4221 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4224 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4226 if (setsel->_getsel_todo_wine) {
4227 todo_wine {
4228 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);
4230 } else {
4231 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);
4235 static void test_EM_EXSETSEL(void)
4237 HWND hwndRichEdit = new_richedit(NULL);
4238 int i;
4239 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4241 /* sending some text to the window */
4242 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4243 /* 01234567890123456*/
4244 /* 10 */
4246 for (i = 0; i < num_tests; i++) {
4247 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4250 DestroyWindow(hwndRichEdit);
4253 static void test_EM_REPLACESEL(int redraw)
4255 HWND hwndRichEdit = new_richedit(NULL);
4256 char buffer[1024] = {0};
4257 int r;
4258 GETTEXTEX getText;
4259 CHARRANGE cr;
4261 /* sending some text to the window */
4262 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4263 /* 01234567890123456*/
4264 /* 10 */
4266 /* FIXME add more tests */
4267 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4268 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4269 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4270 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4271 r = strcmp(buffer, "testing");
4272 ok(0 == r, "expected %d, got %d\n", 0, r);
4274 DestroyWindow(hwndRichEdit);
4276 hwndRichEdit = new_richedit(NULL);
4278 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4279 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4281 /* Test behavior with carriage returns and newlines */
4282 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4283 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4284 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4285 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4286 r = strcmp(buffer, "RichEdit1");
4287 ok(0 == r, "expected %d, got %d\n", 0, r);
4288 getText.cb = 1024;
4289 getText.codepage = CP_ACP;
4290 getText.flags = GT_DEFAULT;
4291 getText.lpDefaultChar = NULL;
4292 getText.lpUsedDefChar = NULL;
4293 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4294 ok(strcmp(buffer, "RichEdit1") == 0,
4295 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4297 /* Test number of lines reported after EM_REPLACESEL */
4298 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4299 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4301 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4302 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4303 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4304 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4305 r = strcmp(buffer, "RichEdit1\r\n");
4306 ok(0 == r, "expected %d, got %d\n", 0, r);
4307 getText.cb = 1024;
4308 getText.codepage = CP_ACP;
4309 getText.flags = GT_DEFAULT;
4310 getText.lpDefaultChar = NULL;
4311 getText.lpUsedDefChar = NULL;
4312 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4313 ok(strcmp(buffer, "RichEdit1\r") == 0,
4314 "EM_GETTEXTEX returned incorrect string\n");
4316 /* Test number of lines reported after EM_REPLACESEL */
4317 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4318 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4320 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4321 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4322 returns the number of characters *inserted* into the control (after
4323 required conversions), but WinXP's riched20 returns the number of
4324 characters interpreted from the original lParam. Wine's builtin riched20
4325 implements the WinXP behavior.
4327 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4328 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4329 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4330 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4332 /* Test number of lines reported after EM_REPLACESEL */
4333 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4334 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4336 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4337 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4338 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4339 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4341 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4342 r = strcmp(buffer, "RichEdit1\r\n");
4343 ok(0 == r, "expected %d, got %d\n", 0, r);
4344 getText.cb = 1024;
4345 getText.codepage = CP_ACP;
4346 getText.flags = GT_DEFAULT;
4347 getText.lpDefaultChar = NULL;
4348 getText.lpUsedDefChar = NULL;
4349 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4350 ok(strcmp(buffer, "RichEdit1\r") == 0,
4351 "EM_GETTEXTEX returned incorrect string\n");
4353 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4354 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4355 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4356 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4358 /* The following tests show that richedit should handle the special \r\r\n
4359 sequence by turning it into a single space on insertion. However,
4360 EM_REPLACESEL on WinXP returns the number of characters in the original
4361 string.
4364 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4365 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4366 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4367 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4368 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4369 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4370 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4372 /* Test the actual string */
4373 getText.cb = 1024;
4374 getText.codepage = CP_ACP;
4375 getText.flags = GT_DEFAULT;
4376 getText.lpDefaultChar = NULL;
4377 getText.lpUsedDefChar = NULL;
4378 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4379 ok(strcmp(buffer, "\r\r") == 0,
4380 "EM_GETTEXTEX returned incorrect string\n");
4382 /* Test number of lines reported after EM_REPLACESEL */
4383 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4384 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4386 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4387 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4388 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4389 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4390 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4391 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4392 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4393 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4395 /* Test the actual string */
4396 getText.cb = 1024;
4397 getText.codepage = CP_ACP;
4398 getText.flags = GT_DEFAULT;
4399 getText.lpDefaultChar = NULL;
4400 getText.lpUsedDefChar = NULL;
4401 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4402 ok(strcmp(buffer, " ") == 0,
4403 "EM_GETTEXTEX returned incorrect string\n");
4405 /* Test number of lines reported after EM_REPLACESEL */
4406 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4407 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4409 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4410 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4411 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4412 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4413 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4414 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4415 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4416 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4418 /* Test the actual string */
4419 getText.cb = 1024;
4420 getText.codepage = CP_ACP;
4421 getText.flags = GT_DEFAULT;
4422 getText.lpDefaultChar = NULL;
4423 getText.lpUsedDefChar = NULL;
4424 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4425 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4426 "EM_GETTEXTEX returned incorrect string\n");
4428 /* Test number of lines reported after EM_REPLACESEL */
4429 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4430 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4432 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4433 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4434 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4435 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4436 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4437 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4438 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4439 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4441 /* Test the actual string */
4442 getText.cb = 1024;
4443 getText.codepage = CP_ACP;
4444 getText.flags = GT_DEFAULT;
4445 getText.lpDefaultChar = NULL;
4446 getText.lpUsedDefChar = NULL;
4447 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4448 ok(strcmp(buffer, " \r") == 0,
4449 "EM_GETTEXTEX returned incorrect string\n");
4451 /* Test number of lines reported after EM_REPLACESEL */
4452 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4453 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4455 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4456 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4457 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4458 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4459 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4460 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4461 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4462 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4464 /* Test the actual string */
4465 getText.cb = 1024;
4466 getText.codepage = CP_ACP;
4467 getText.flags = GT_DEFAULT;
4468 getText.lpDefaultChar = NULL;
4469 getText.lpUsedDefChar = NULL;
4470 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4471 ok(strcmp(buffer, " \r\r") == 0,
4472 "EM_GETTEXTEX returned incorrect string\n");
4474 /* Test number of lines reported after EM_REPLACESEL */
4475 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4476 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4478 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4479 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4480 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4481 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4482 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4483 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4484 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4485 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4487 /* Test the actual string */
4488 getText.cb = 1024;
4489 getText.codepage = CP_ACP;
4490 getText.flags = GT_DEFAULT;
4491 getText.lpDefaultChar = NULL;
4492 getText.lpUsedDefChar = NULL;
4493 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4494 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4495 "EM_GETTEXTEX returned incorrect string\n");
4497 /* Test number of lines reported after EM_REPLACESEL */
4498 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4499 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4501 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4502 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4503 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4504 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4505 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4506 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4507 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4509 /* Test the actual string */
4510 getText.cb = 1024;
4511 getText.codepage = CP_ACP;
4512 getText.flags = GT_DEFAULT;
4513 getText.lpDefaultChar = NULL;
4514 getText.lpUsedDefChar = NULL;
4515 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4516 ok(strcmp(buffer, "\r\r") == 0,
4517 "EM_GETTEXTEX returned incorrect string\n");
4519 /* Test number of lines reported after EM_REPLACESEL */
4520 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4521 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4523 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4524 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4525 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4526 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4527 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4528 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4529 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4530 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4532 /* Test the actual string */
4533 getText.cb = 1024;
4534 getText.codepage = CP_ACP;
4535 getText.flags = GT_DEFAULT;
4536 getText.lpDefaultChar = NULL;
4537 getText.lpUsedDefChar = NULL;
4538 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4539 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4540 "EM_GETTEXTEX returned incorrect string\n");
4542 /* Test number of lines reported after EM_REPLACESEL */
4543 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4544 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4546 if (!redraw)
4547 /* This is needed to avoid interferring with keybd_event calls
4548 * on other tests that simulate keyboard events. */
4549 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4551 DestroyWindow(hwndRichEdit);
4554 static void test_WM_PASTE(void)
4556 int result;
4557 char buffer[1024] = {0};
4558 const char* text1 = "testing paste\r";
4559 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4560 const char* text1_after = "testing paste\r\n";
4561 const char* text2 = "testing paste\r\rtesting paste";
4562 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4563 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4564 HWND hwndRichEdit = new_richedit(NULL);
4566 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4567 * to test the state of the modifiers (Ctrl/Alt/Shift).
4569 * Therefore Ctrl-<key> keystrokes need to be simulated with
4570 * keybd_event or by using SetKeyboardState to set the modifiers
4571 * and SendMessage to simulate the keystrokes.
4574 /* Sent keystrokes with keybd_event */
4575 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4576 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4577 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4578 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4579 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4581 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4582 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4584 SEND_CTRL_C(hwndRichEdit); /* Copy */
4585 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4586 SEND_CTRL_V(hwndRichEdit); /* Paste */
4587 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4588 /* Pasted text should be visible at this step */
4589 result = strcmp(text1_step1, buffer);
4590 ok(result == 0,
4591 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4593 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4594 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4595 /* Text should be the same as before (except for \r -> \r\n conversion) */
4596 result = strcmp(text1_after, buffer);
4597 ok(result == 0,
4598 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4600 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4601 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4602 SEND_CTRL_C(hwndRichEdit); /* Copy */
4603 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4604 SEND_CTRL_V(hwndRichEdit); /* Paste */
4605 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4606 /* Pasted text should be visible at this step */
4607 result = strcmp(text3, buffer);
4608 ok(result == 0,
4609 "test paste: strcmp = %i\n", result);
4610 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4611 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4612 /* Text should be the same as before (except for \r -> \r\n conversion) */
4613 result = strcmp(text2_after, buffer);
4614 ok(result == 0,
4615 "test paste: strcmp = %i\n", result);
4616 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4617 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4618 /* Text should revert to post-paste state */
4619 result = strcmp(buffer,text3);
4620 ok(result == 0,
4621 "test paste: strcmp = %i\n", result);
4623 #undef SEND_CTRL_C
4624 #undef SEND_CTRL_X
4625 #undef SEND_CTRL_V
4626 #undef SEND_CTRL_Z
4627 #undef SEND_CTRL_Y
4629 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4630 /* Send WM_CHAR to simulates Ctrl-V */
4631 SendMessage(hwndRichEdit, WM_CHAR, 22,
4632 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4633 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4634 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4635 result = strcmp(buffer,"");
4636 ok(result == 0,
4637 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4639 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4640 * with SetKeyboard state. */
4642 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4643 /* Simulates paste (Ctrl-V) */
4644 hold_key(VK_CONTROL);
4645 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4646 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4647 release_key(VK_CONTROL);
4648 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4649 result = strcmp(buffer,"paste");
4650 ok(result == 0,
4651 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4653 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4654 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4655 /* Simulates copy (Ctrl-C) */
4656 hold_key(VK_CONTROL);
4657 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4658 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4659 release_key(VK_CONTROL);
4660 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4661 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4662 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4663 result = strcmp(buffer,"testing");
4664 ok(result == 0,
4665 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4667 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4668 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4669 /* Simulates select all (Ctrl-A) */
4670 hold_key(VK_CONTROL);
4671 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4672 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4673 /* Simulates select cut (Ctrl-X) */
4674 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4675 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4676 release_key(VK_CONTROL);
4677 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4678 result = strcmp(buffer,"");
4679 ok(result == 0,
4680 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4681 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4682 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4683 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4684 result = strcmp(buffer,"cut\r\n");
4685 todo_wine ok(result == 0,
4686 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4687 /* Simulates undo (Ctrl-Z) */
4688 hold_key(VK_CONTROL);
4689 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4690 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4691 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4692 result = strcmp(buffer,"");
4693 ok(result == 0,
4694 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4695 /* Simulates redo (Ctrl-Y) */
4696 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4697 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4698 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4699 result = strcmp(buffer,"cut\r\n");
4700 todo_wine ok(result == 0,
4701 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4702 release_key(VK_CONTROL);
4704 DestroyWindow(hwndRichEdit);
4707 static void test_EM_FORMATRANGE(void)
4709 int r;
4710 FORMATRANGE fr;
4711 HDC hdc;
4712 HWND hwndRichEdit = new_richedit(NULL);
4714 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4716 hdc = GetDC(hwndRichEdit);
4717 ok(hdc != NULL, "Could not get HDC\n");
4719 fr.hdc = fr.hdcTarget = hdc;
4720 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4721 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4722 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4723 fr.chrg.cpMin = 0;
4724 fr.chrg.cpMax = 20;
4726 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4727 todo_wine {
4728 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4731 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4732 todo_wine {
4733 ok(r == 20 || r == 9, "EM_FORMATRANGE expect 20 or 9, got %d\n", r);
4736 fr.chrg.cpMin = 0;
4737 fr.chrg.cpMax = 10;
4739 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4740 todo_wine {
4741 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4744 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4745 todo_wine {
4746 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4749 DestroyWindow(hwndRichEdit);
4752 static int nCallbackCount = 0;
4754 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4755 LONG cb, LONG* pcb)
4757 const char text[] = {'t','e','s','t'};
4759 if (sizeof(text) <= cb)
4761 if ((int)dwCookie != nCallbackCount)
4763 *pcb = 0;
4764 return 0;
4767 memcpy (pbBuff, text, sizeof(text));
4768 *pcb = sizeof(text);
4770 nCallbackCount++;
4772 return 0;
4774 else
4775 return 1; /* indicates callback failed */
4778 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4779 LPBYTE pbBuff,
4780 LONG cb,
4781 LONG *pcb)
4783 const char** str = (const char**)dwCookie;
4784 int size = strlen(*str);
4785 *pcb = cb;
4786 if (*pcb > size) {
4787 *pcb = size;
4789 if (*pcb > 0) {
4790 memcpy(pbBuff, *str, *pcb);
4791 *str += *pcb;
4793 return 0;
4796 struct StringWithLength {
4797 int length;
4798 char *buffer;
4801 /* This callback is used to handled the null characters in a string. */
4802 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4803 LPBYTE pbBuff,
4804 LONG cb,
4805 LONG *pcb)
4807 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4808 int size = str->length;
4809 *pcb = cb;
4810 if (*pcb > size) {
4811 *pcb = size;
4813 if (*pcb > 0) {
4814 memcpy(pbBuff, str->buffer, *pcb);
4815 str->buffer += *pcb;
4816 str->length -= *pcb;
4818 return 0;
4821 static void test_EM_STREAMIN(void)
4823 HWND hwndRichEdit = new_richedit(NULL);
4824 LRESULT result;
4825 EDITSTREAM es;
4826 char buffer[1024] = {0};
4828 const char * streamText0 = "{\\rtf1 TestSomeText}";
4829 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4830 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4832 const char * streamText1 =
4833 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4834 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
4835 "}\r\n";
4837 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4838 const char * streamText2 =
4839 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
4840 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
4841 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
4842 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
4843 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
4844 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
4845 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4847 const char * streamText3 = "RichEdit1";
4849 struct StringWithLength cookieForStream4;
4850 const char * streamText4 =
4851 "This text just needs to be long enough to cause run to be split onto "
4852 "two separate lines and make sure the null terminating character is "
4853 "handled properly.\0";
4854 int length4 = strlen(streamText4) + 1;
4855 cookieForStream4.buffer = (char *)streamText4;
4856 cookieForStream4.length = length4;
4858 /* Minimal test without \par at the end */
4859 es.dwCookie = (DWORD_PTR)&streamText0;
4860 es.dwError = 0;
4861 es.pfnCallback = test_EM_STREAMIN_esCallback;
4862 SendMessage(hwndRichEdit, EM_STREAMIN,
4863 (WPARAM)(SF_RTF), (LPARAM)&es);
4865 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4866 ok (result == 12,
4867 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4868 result = strcmp (buffer,"TestSomeText");
4869 ok (result == 0,
4870 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4871 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4873 /* Native richedit 2.0 ignores last \par */
4874 es.dwCookie = (DWORD_PTR)&streamText0a;
4875 es.dwError = 0;
4876 es.pfnCallback = test_EM_STREAMIN_esCallback;
4877 SendMessage(hwndRichEdit, EM_STREAMIN,
4878 (WPARAM)(SF_RTF), (LPARAM)&es);
4880 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4881 ok (result == 12,
4882 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4883 result = strcmp (buffer,"TestSomeText");
4884 ok (result == 0,
4885 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4886 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4888 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4889 es.dwCookie = (DWORD_PTR)&streamText0b;
4890 es.dwError = 0;
4891 es.pfnCallback = test_EM_STREAMIN_esCallback;
4892 SendMessage(hwndRichEdit, EM_STREAMIN,
4893 (WPARAM)(SF_RTF), (LPARAM)&es);
4895 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4896 ok (result == 14,
4897 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4898 result = strcmp (buffer,"TestSomeText\r\n");
4899 ok (result == 0,
4900 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4901 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4903 es.dwCookie = (DWORD_PTR)&streamText1;
4904 es.dwError = 0;
4905 es.pfnCallback = test_EM_STREAMIN_esCallback;
4906 SendMessage(hwndRichEdit, EM_STREAMIN,
4907 (WPARAM)(SF_RTF), (LPARAM)&es);
4909 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4910 ok (result == 12,
4911 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4912 result = strcmp (buffer,"TestSomeText");
4913 ok (result == 0,
4914 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4915 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4917 es.dwCookie = (DWORD_PTR)&streamText2;
4918 es.dwError = 0;
4919 SendMessage(hwndRichEdit, EM_STREAMIN,
4920 (WPARAM)(SF_RTF), (LPARAM)&es);
4922 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4923 ok (result == 0,
4924 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4925 ok (strlen(buffer) == 0,
4926 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4927 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4929 es.dwCookie = (DWORD_PTR)&streamText3;
4930 es.dwError = 0;
4931 SendMessage(hwndRichEdit, EM_STREAMIN,
4932 (WPARAM)(SF_RTF), (LPARAM)&es);
4934 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4935 ok (result == 0,
4936 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4937 ok (strlen(buffer) == 0,
4938 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4939 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4941 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4942 es.dwError = 0;
4943 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4944 SendMessage(hwndRichEdit, EM_STREAMIN,
4945 (WPARAM)(SF_TEXT), (LPARAM)&es);
4947 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4948 ok (result == length4,
4949 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4950 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4952 DestroyWindow(hwndRichEdit);
4955 static void test_EM_StreamIn_Undo(void)
4957 /* The purpose of this test is to determine when a EM_StreamIn should be
4958 * undoable. This is important because WM_PASTE currently uses StreamIn and
4959 * pasting should always be undoable but streaming isn't always.
4961 * cases to test:
4962 * StreamIn plain text without SFF_SELECTION.
4963 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4964 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4965 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4966 * Feel free to add tests for other text modes or StreamIn things.
4970 HWND hwndRichEdit = new_richedit(NULL);
4971 LRESULT result;
4972 EDITSTREAM es;
4973 char buffer[1024] = {0};
4974 const char randomtext[] = "Some text";
4976 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4978 /* StreamIn, no SFF_SELECTION */
4979 es.dwCookie = nCallbackCount;
4980 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4981 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4982 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4983 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4984 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4985 result = strcmp (buffer,"test");
4986 ok (result == 0,
4987 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4989 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4990 ok (result == FALSE,
4991 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4993 /* StreamIn, SFF_SELECTION, but nothing selected */
4994 es.dwCookie = nCallbackCount;
4995 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4996 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4997 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4998 SendMessage(hwndRichEdit, EM_STREAMIN,
4999 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5000 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5001 result = strcmp (buffer,"testSome text");
5002 ok (result == 0,
5003 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5005 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5006 ok (result == TRUE,
5007 "EM_STREAMIN with SFF_SELECTION but no selection set "
5008 "should create an undo\n");
5010 /* StreamIn, SFF_SELECTION, with a selection */
5011 es.dwCookie = nCallbackCount;
5012 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5013 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5014 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5015 SendMessage(hwndRichEdit, EM_STREAMIN,
5016 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5017 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5018 result = strcmp (buffer,"Sometesttext");
5019 ok (result == 0,
5020 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5022 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5023 ok (result == TRUE,
5024 "EM_STREAMIN with SFF_SELECTION and selection set "
5025 "should create an undo\n");
5027 DestroyWindow(hwndRichEdit);
5030 static BOOL is_em_settextex_supported(HWND hwnd)
5032 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5033 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5036 static void test_unicode_conversions(void)
5038 static const WCHAR tW[] = {'t',0};
5039 static const WCHAR teW[] = {'t','e',0};
5040 static const WCHAR textW[] = {'t','e','s','t',0};
5041 static const char textA[] = "test";
5042 char bufA[64];
5043 WCHAR bufW[64];
5044 HWND hwnd;
5045 int em_settextex_supported, ret;
5047 #define set_textA(hwnd, wm_set_text, txt) \
5048 do { \
5049 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5050 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5051 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5052 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5053 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5054 } while(0)
5055 #define expect_textA(hwnd, wm_get_text, txt) \
5056 do { \
5057 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5058 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5059 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5060 memset(bufA, 0xAA, sizeof(bufA)); \
5061 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5062 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5063 ret = lstrcmpA(bufA, txt); \
5064 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5065 } while(0)
5067 #define set_textW(hwnd, wm_set_text, txt) \
5068 do { \
5069 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5070 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5071 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5072 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5073 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5074 } while(0)
5075 #define expect_textW(hwnd, wm_get_text, txt) \
5076 do { \
5077 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5078 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5079 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5080 memset(bufW, 0xAA, sizeof(bufW)); \
5081 if (is_win9x) \
5083 assert(wm_get_text == EM_GETTEXTEX); \
5084 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5085 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5087 else \
5089 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5090 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5092 ret = lstrcmpW(bufW, txt); \
5093 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5094 } while(0)
5095 #define expect_empty(hwnd, wm_get_text) \
5096 do { \
5097 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5098 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5099 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5100 memset(bufA, 0xAA, sizeof(bufA)); \
5101 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5102 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5103 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5104 } while(0)
5106 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5107 0, 0, 200, 60, 0, 0, 0, 0);
5108 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5110 ret = IsWindowUnicode(hwnd);
5111 if (is_win9x)
5112 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
5113 else
5114 ok(ret, "RichEdit20W should be unicode under NT\n");
5116 /* EM_SETTEXTEX is supported starting from version 3.0 */
5117 em_settextex_supported = is_em_settextex_supported(hwnd);
5118 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5119 em_settextex_supported ? "" : "NOT ");
5121 expect_empty(hwnd, WM_GETTEXT);
5122 expect_empty(hwnd, EM_GETTEXTEX);
5124 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
5125 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5126 expect_textA(hwnd, WM_GETTEXT, "t");
5127 expect_textA(hwnd, EM_GETTEXTEX, "t");
5128 expect_textW(hwnd, EM_GETTEXTEX, tW);
5130 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
5131 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5132 expect_textA(hwnd, WM_GETTEXT, "te");
5133 expect_textA(hwnd, EM_GETTEXTEX, "te");
5134 expect_textW(hwnd, EM_GETTEXTEX, teW);
5136 set_textA(hwnd, WM_SETTEXT, NULL);
5137 expect_empty(hwnd, WM_GETTEXT);
5138 expect_empty(hwnd, EM_GETTEXTEX);
5140 if (is_win9x)
5141 set_textA(hwnd, WM_SETTEXT, textW);
5142 else
5143 set_textA(hwnd, WM_SETTEXT, textA);
5144 expect_textA(hwnd, WM_GETTEXT, textA);
5145 expect_textA(hwnd, EM_GETTEXTEX, textA);
5146 expect_textW(hwnd, EM_GETTEXTEX, textW);
5148 if (em_settextex_supported)
5150 set_textA(hwnd, EM_SETTEXTEX, textA);
5151 expect_textA(hwnd, WM_GETTEXT, textA);
5152 expect_textA(hwnd, EM_GETTEXTEX, textA);
5153 expect_textW(hwnd, EM_GETTEXTEX, textW);
5156 if (!is_win9x)
5158 set_textW(hwnd, WM_SETTEXT, textW);
5159 expect_textW(hwnd, WM_GETTEXT, textW);
5160 expect_textA(hwnd, WM_GETTEXT, textA);
5161 expect_textW(hwnd, EM_GETTEXTEX, textW);
5162 expect_textA(hwnd, EM_GETTEXTEX, textA);
5164 if (em_settextex_supported)
5166 set_textW(hwnd, EM_SETTEXTEX, textW);
5167 expect_textW(hwnd, WM_GETTEXT, textW);
5168 expect_textA(hwnd, WM_GETTEXT, textA);
5169 expect_textW(hwnd, EM_GETTEXTEX, textW);
5170 expect_textA(hwnd, EM_GETTEXTEX, textA);
5173 DestroyWindow(hwnd);
5175 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5176 0, 0, 200, 60, 0, 0, 0, 0);
5177 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5179 ret = IsWindowUnicode(hwnd);
5180 ok(!ret, "RichEdit20A should NOT be unicode\n");
5182 set_textA(hwnd, WM_SETTEXT, textA);
5183 expect_textA(hwnd, WM_GETTEXT, textA);
5184 expect_textA(hwnd, EM_GETTEXTEX, textA);
5185 expect_textW(hwnd, EM_GETTEXTEX, textW);
5187 if (em_settextex_supported)
5189 set_textA(hwnd, EM_SETTEXTEX, textA);
5190 expect_textA(hwnd, WM_GETTEXT, textA);
5191 expect_textA(hwnd, EM_GETTEXTEX, textA);
5192 expect_textW(hwnd, EM_GETTEXTEX, textW);
5195 if (!is_win9x)
5197 set_textW(hwnd, WM_SETTEXT, textW);
5198 expect_textW(hwnd, WM_GETTEXT, textW);
5199 expect_textA(hwnd, WM_GETTEXT, textA);
5200 expect_textW(hwnd, EM_GETTEXTEX, textW);
5201 expect_textA(hwnd, EM_GETTEXTEX, textA);
5203 if (em_settextex_supported)
5205 set_textW(hwnd, EM_SETTEXTEX, textW);
5206 expect_textW(hwnd, WM_GETTEXT, textW);
5207 expect_textA(hwnd, WM_GETTEXT, textA);
5208 expect_textW(hwnd, EM_GETTEXTEX, textW);
5209 expect_textA(hwnd, EM_GETTEXTEX, textA);
5212 DestroyWindow(hwnd);
5215 static void test_WM_CHAR(void)
5217 HWND hwnd;
5218 int ret;
5219 const char * char_list = "abc\rabc\r";
5220 const char * expected_content_single = "abcabc";
5221 const char * expected_content_multi = "abc\r\nabc\r\n";
5222 char buffer[64] = {0};
5223 const char * p;
5225 /* single-line control must IGNORE carriage returns */
5226 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5227 0, 0, 200, 60, 0, 0, 0, 0);
5228 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5230 p = char_list;
5231 while (*p != '\0') {
5232 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5233 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5234 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5235 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5236 p++;
5239 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5240 ret = strcmp(buffer, expected_content_single);
5241 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5243 DestroyWindow(hwnd);
5245 /* multi-line control inserts CR normally */
5246 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5247 0, 0, 200, 60, 0, 0, 0, 0);
5248 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5250 p = char_list;
5251 while (*p != '\0') {
5252 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5253 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5254 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5255 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5256 p++;
5259 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5260 ret = strcmp(buffer, expected_content_multi);
5261 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5263 DestroyWindow(hwnd);
5266 static void test_EM_GETTEXTLENGTHEX(void)
5268 HWND hwnd;
5269 GETTEXTLENGTHEX gtl;
5270 int ret;
5271 const char * base_string = "base string";
5272 const char * test_string = "a\nb\n\n\r\n";
5273 const char * test_string_after = "a";
5274 const char * test_string_2 = "a\rtest\rstring";
5275 char buffer[64] = {0};
5277 /* single line */
5278 if (!is_win9x)
5279 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5280 0, 0, 200, 60, 0, 0, 0, 0);
5281 else
5282 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5283 0, 0, 200, 60, 0, 0, 0, 0);
5284 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5286 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5287 gtl.codepage = CP_ACP;
5288 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5289 ok(ret == 0, "ret %d\n",ret);
5291 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5292 gtl.codepage = CP_ACP;
5293 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5294 ok(ret == 0, "ret %d\n",ret);
5296 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5298 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5299 gtl.codepage = CP_ACP;
5300 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5301 ok(ret == strlen(base_string), "ret %d\n",ret);
5303 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5304 gtl.codepage = CP_ACP;
5305 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5306 ok(ret == strlen(base_string), "ret %d\n",ret);
5308 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5310 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5311 gtl.codepage = CP_ACP;
5312 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5313 ok(ret == 1, "ret %d\n",ret);
5315 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5316 gtl.codepage = CP_ACP;
5317 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5318 ok(ret == 1, "ret %d\n",ret);
5320 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5321 ret = strcmp(buffer, test_string_after);
5322 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5324 DestroyWindow(hwnd);
5326 /* multi line */
5327 if (!is_win9x)
5328 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5329 0, 0, 200, 60, 0, 0, 0, 0);
5330 else
5331 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE,
5332 0, 0, 200, 60, 0, 0, 0, 0);
5333 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5335 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5336 gtl.codepage = CP_ACP;
5337 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5338 ok(ret == 0, "ret %d\n",ret);
5340 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5341 gtl.codepage = CP_ACP;
5342 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5343 ok(ret == 0, "ret %d\n",ret);
5345 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5347 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5348 gtl.codepage = CP_ACP;
5349 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5350 ok(ret == strlen(base_string), "ret %d\n",ret);
5352 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5353 gtl.codepage = CP_ACP;
5354 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5355 ok(ret == strlen(base_string), "ret %d\n",ret);
5357 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5359 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5360 gtl.codepage = CP_ACP;
5361 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5362 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5364 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5365 gtl.codepage = CP_ACP;
5366 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5367 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5369 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5371 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5372 gtl.codepage = CP_ACP;
5373 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5374 ok(ret == 10, "ret %d\n",ret);
5376 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5377 gtl.codepage = CP_ACP;
5378 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5379 ok(ret == 6, "ret %d\n",ret);
5381 DestroyWindow(hwnd);
5385 /* globals that parent and child access when checking event masks & notifications */
5386 static HWND eventMaskEditHwnd = 0;
5387 static int queriedEventMask;
5388 static int watchForEventMask = 0;
5390 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5391 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5393 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5395 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5397 return DefWindowProcA(hwnd, message, wParam, lParam);
5400 /* test event masks in combination with WM_COMMAND */
5401 static void test_eventMask(void)
5403 HWND parent;
5404 int ret, style;
5405 WNDCLASSA cls;
5406 const char text[] = "foo bar\n";
5407 int eventMask;
5409 /* register class to capture WM_COMMAND */
5410 cls.style = 0;
5411 cls.lpfnWndProc = ParentMsgCheckProcA;
5412 cls.cbClsExtra = 0;
5413 cls.cbWndExtra = 0;
5414 cls.hInstance = GetModuleHandleA(0);
5415 cls.hIcon = 0;
5416 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5417 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5418 cls.lpszMenuName = NULL;
5419 cls.lpszClassName = "EventMaskParentClass";
5420 if(!RegisterClassA(&cls)) assert(0);
5422 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5423 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5424 ok (parent != 0, "Failed to create parent window\n");
5426 eventMaskEditHwnd = new_richedit(parent);
5427 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5429 eventMask = ENM_CHANGE | ENM_UPDATE;
5430 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5431 ok(ret == ENM_NONE, "wrong event mask\n");
5432 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5433 ok(ret == eventMask, "failed to set event mask\n");
5435 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5436 queriedEventMask = 0; /* initialize to something other than we expect */
5437 watchForEventMask = EN_CHANGE;
5438 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5439 ok(ret == TRUE, "failed to set text\n");
5440 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5441 notification in response to WM_SETTEXT */
5442 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5443 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5445 /* check to see if EN_CHANGE is sent when redraw is turned off */
5446 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5447 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5448 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5449 /* redraw is disabled by making the window invisible. */
5450 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5451 queriedEventMask = 0; /* initialize to something other than we expect */
5452 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5453 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5454 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5455 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5456 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5458 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5459 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5460 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5461 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5462 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5463 watchForEventMask = EN_UPDATE;
5464 queriedEventMask = 0; /* initialize to something other than we expect */
5465 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5466 ok(queriedEventMask == 0,
5467 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5468 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5469 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5470 queriedEventMask = 0; /* initialize to something other than we expect */
5471 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5472 ok(queriedEventMask == eventMask,
5473 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5476 DestroyWindow(parent);
5479 static int received_WM_NOTIFY = 0;
5480 static int modify_at_WM_NOTIFY = 0;
5481 static HWND hwndRichedit_WM_NOTIFY;
5483 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5485 if(message == WM_NOTIFY)
5487 received_WM_NOTIFY = 1;
5488 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5490 return DefWindowProcA(hwnd, message, wParam, lParam);
5493 static void test_WM_NOTIFY(void)
5495 HWND parent;
5496 WNDCLASSA cls;
5497 CHARFORMAT2 cf2;
5499 /* register class to capture WM_NOTIFY */
5500 cls.style = 0;
5501 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5502 cls.cbClsExtra = 0;
5503 cls.cbWndExtra = 0;
5504 cls.hInstance = GetModuleHandleA(0);
5505 cls.hIcon = 0;
5506 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5507 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5508 cls.lpszMenuName = NULL;
5509 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5510 if(!RegisterClassA(&cls)) assert(0);
5512 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5513 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5514 ok (parent != 0, "Failed to create parent window\n");
5516 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5517 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5519 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5521 /* Notifications for selection change should only be sent when selection
5522 actually changes. EM_SETCHARFORMAT is one message that calls
5523 ME_CommitUndo, which should check whether message should be sent */
5524 received_WM_NOTIFY = 0;
5525 cf2.cbSize = sizeof(CHARFORMAT2);
5526 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5527 (LPARAM) &cf2);
5528 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5529 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5530 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5531 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5533 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5534 already at 0. */
5535 received_WM_NOTIFY = 0;
5536 modify_at_WM_NOTIFY = 0;
5537 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5538 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5539 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5541 received_WM_NOTIFY = 0;
5542 modify_at_WM_NOTIFY = 0;
5543 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5544 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5546 received_WM_NOTIFY = 0;
5547 modify_at_WM_NOTIFY = 0;
5548 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5549 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5550 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5552 /* Test for WM_NOTIFY messages with redraw disabled. */
5553 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5554 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5555 received_WM_NOTIFY = 0;
5556 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5557 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5558 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5560 DestroyWindow(hwndRichedit_WM_NOTIFY);
5561 DestroyWindow(parent);
5564 static void test_undo_coalescing(void)
5566 HWND hwnd;
5567 int result;
5568 char buffer[64] = {0};
5570 /* multi-line control inserts CR normally */
5571 if (!is_win9x)
5572 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5573 0, 0, 200, 60, 0, 0, 0, 0);
5574 else
5575 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE,
5576 0, 0, 200, 60, 0, 0, 0, 0);
5577 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5579 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5580 ok (result == FALSE, "Can undo after window creation.\n");
5581 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5582 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5583 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5584 ok (result == FALSE, "Can redo after window creation.\n");
5585 result = SendMessage(hwnd, EM_REDO, 0, 0);
5586 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5588 /* Test the effect of arrows keys during typing on undo transactions*/
5589 simulate_typing_characters(hwnd, "one two three");
5590 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5591 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5592 simulate_typing_characters(hwnd, " four five six");
5594 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5595 ok (result == FALSE, "Can redo before anything is undone.\n");
5596 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5597 ok (result == TRUE, "Cannot undo typed characters.\n");
5598 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5599 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5600 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5601 ok (result == TRUE, "Cannot redo after undo.\n");
5602 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5603 result = strcmp(buffer, "one two three");
5604 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5606 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5607 ok (result == TRUE, "Cannot undo typed characters.\n");
5608 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5609 ok (result == TRUE, "Failed to undo typed characters.\n");
5610 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5611 result = strcmp(buffer, "");
5612 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5614 /* Test the effect of focus changes during typing on undo transactions*/
5615 simulate_typing_characters(hwnd, "one two three");
5616 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5617 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5618 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5619 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5620 simulate_typing_characters(hwnd, " four five six");
5621 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5622 ok (result == TRUE, "Failed to undo typed characters.\n");
5623 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5624 result = strcmp(buffer, "one two three");
5625 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5627 /* Test the effect of the back key during typing on undo transactions */
5628 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5629 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5630 ok (result == TRUE, "Failed to clear the text.\n");
5631 simulate_typing_characters(hwnd, "one two threa");
5632 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5633 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5634 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5635 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5636 simulate_typing_characters(hwnd, "e four five six");
5637 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5638 ok (result == TRUE, "Failed to undo typed characters.\n");
5639 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5640 result = strcmp(buffer, "");
5641 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5643 /* Test the effect of the delete key during typing on undo transactions */
5644 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5645 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5646 ok(result == TRUE, "Failed to set the text.\n");
5647 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5648 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5649 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5650 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5651 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5652 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5653 ok (result == TRUE, "Failed to undo typed characters.\n");
5654 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5655 result = strcmp(buffer, "acd");
5656 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5657 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5658 ok (result == TRUE, "Failed to undo typed characters.\n");
5659 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5660 result = strcmp(buffer, "abcd");
5661 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5663 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5664 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5665 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5666 ok (result == TRUE, "Failed to clear the text.\n");
5667 simulate_typing_characters(hwnd, "one two three");
5668 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5669 ok (result == 0, "expected %d but got %d\n", 0, result);
5670 simulate_typing_characters(hwnd, " four five six");
5671 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5672 ok (result == TRUE, "Failed to undo typed characters.\n");
5673 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5674 result = strcmp(buffer, "one two three");
5675 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5676 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5677 ok (result == TRUE, "Failed to undo typed characters.\n");
5678 ok (result == TRUE, "Failed to undo typed characters.\n");
5679 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5680 result = strcmp(buffer, "");
5681 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5683 DestroyWindow(hwnd);
5686 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5688 int length;
5690 /* MSDN lied, length is actually the number of bytes. */
5691 length = bytes / sizeof(WCHAR);
5692 switch(code)
5694 case WB_ISDELIMITER:
5695 return text[pos] == 'X';
5696 case WB_LEFT:
5697 case WB_MOVEWORDLEFT:
5698 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5699 return pos-1;
5700 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5701 case WB_LEFTBREAK:
5702 pos--;
5703 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5704 pos--;
5705 return pos;
5706 case WB_RIGHT:
5707 case WB_MOVEWORDRIGHT:
5708 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5709 return pos+1;
5710 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5711 case WB_RIGHTBREAK:
5712 pos++;
5713 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5714 pos++;
5715 return pos;
5716 default:
5717 ok(FALSE, "Unexpected code %d\n", code);
5718 break;
5720 return 0;
5723 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5724 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5726 static void test_word_movement(void)
5728 HWND hwnd;
5729 int result;
5730 int sel_start, sel_end;
5731 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5733 /* multi-line control inserts CR normally */
5734 hwnd = new_richedit(NULL);
5736 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5737 ok (result == TRUE, "Failed to clear the text.\n");
5738 SendMessage(hwnd, EM_SETSEL, 0, 0);
5739 /* |one two three */
5741 SEND_CTRL_RIGHT(hwnd);
5742 /* one |two three */
5743 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5744 ok(sel_start == sel_end, "Selection should be empty\n");
5745 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5747 SEND_CTRL_RIGHT(hwnd);
5748 /* one two |three */
5749 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5750 ok(sel_start == sel_end, "Selection should be empty\n");
5751 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5753 SEND_CTRL_LEFT(hwnd);
5754 /* one |two three */
5755 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5756 ok(sel_start == sel_end, "Selection should be empty\n");
5757 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5759 SEND_CTRL_LEFT(hwnd);
5760 /* |one two three */
5761 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5762 ok(sel_start == sel_end, "Selection should be empty\n");
5763 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5765 SendMessage(hwnd, EM_SETSEL, 8, 8);
5766 /* one two | three */
5767 SEND_CTRL_RIGHT(hwnd);
5768 /* one two |three */
5769 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5770 ok(sel_start == sel_end, "Selection should be empty\n");
5771 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5773 SendMessage(hwnd, EM_SETSEL, 11, 11);
5774 /* one two th|ree */
5775 SEND_CTRL_LEFT(hwnd);
5776 /* one two |three */
5777 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5778 ok(sel_start == sel_end, "Selection should be empty\n");
5779 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5781 /* Test with a custom word break procedure that uses X as the delimiter. */
5782 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5783 ok (result == TRUE, "Failed to clear the text.\n");
5784 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5785 /* |one twoXthree */
5786 SEND_CTRL_RIGHT(hwnd);
5787 /* one twoX|three */
5788 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5789 ok(sel_start == sel_end, "Selection should be empty\n");
5790 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5792 DestroyWindow(hwnd);
5794 /* Make sure the behaviour is the same with a unicode richedit window,
5795 * and using unicode functions. */
5796 if (is_win9x)
5798 skip("Cannot test with unicode richedit window\n");
5799 return;
5802 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
5803 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5804 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5806 /* Test with a custom word break procedure that uses X as the delimiter. */
5807 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
5808 ok (result == TRUE, "Failed to clear the text.\n");
5809 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5810 /* |one twoXthree */
5811 SEND_CTRL_RIGHT(hwnd);
5812 /* one twoX|three */
5813 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5814 ok(sel_start == sel_end, "Selection should be empty\n");
5815 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5817 DestroyWindow(hwnd);
5820 static void test_EM_CHARFROMPOS(void)
5822 HWND hwnd;
5823 int result;
5824 RECT rcClient;
5825 POINTL point;
5826 point.x = 0;
5827 point.y = 40;
5829 /* multi-line control inserts CR normally */
5830 hwnd = new_richedit(NULL);
5831 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5832 (LPARAM)"one two three four five six seven\reight");
5834 GetClientRect(hwnd, &rcClient);
5836 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5837 ok(result == 34, "expected character index of 34 but got %d\n", result);
5839 /* Test with points outside the bounds of the richedit control. */
5840 point.x = -1;
5841 point.y = 40;
5842 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5843 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
5845 point.x = 1000;
5846 point.y = 0;
5847 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5848 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
5850 point.x = 1000;
5851 point.y = 40;
5852 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5853 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
5855 point.x = 1000;
5856 point.y = -1;
5857 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5858 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
5860 point.x = 1000;
5861 point.y = rcClient.bottom + 1;
5862 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5863 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
5865 point.x = 1000;
5866 point.y = rcClient.bottom;
5867 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5868 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
5870 DestroyWindow(hwnd);
5873 static void test_word_wrap(void)
5875 HWND hwnd;
5876 POINTL point = {0, 60}; /* This point must be below the first line */
5877 const char *text = "Must be long enough to test line wrapping";
5878 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
5879 int res, pos, lines;
5881 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
5882 * when specified on window creation and set later. */
5883 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5884 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5885 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5886 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5887 ok(res, "WM_SETTEXT failed.\n");
5888 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5889 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5890 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5891 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5893 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
5894 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5895 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5896 DestroyWindow(hwnd);
5898 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
5899 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5900 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5902 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5903 ok(res, "WM_SETTEXT failed.\n");
5904 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5905 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5906 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5907 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5909 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5910 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5911 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5912 DestroyWindow(hwnd);
5914 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
5915 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5916 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5917 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5918 ok(res, "WM_SETTEXT failed.\n");
5919 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5920 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5922 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5923 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5924 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5925 DestroyWindow(hwnd);
5927 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
5928 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
5929 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5930 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5931 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5932 ok(res, "WM_SETTEXT failed.\n");
5933 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5934 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5936 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5937 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5938 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5940 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
5941 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
5942 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5943 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5944 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5946 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
5947 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5948 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5949 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5950 DestroyWindow(hwnd);
5952 /* Test to see if wrapping happens with redraw disabled. */
5953 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5954 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
5955 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5956 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
5957 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
5958 ok(res, "EM_REPLACESEL failed.\n");
5959 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5960 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5961 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
5962 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5963 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5965 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5966 DestroyWindow(hwnd);
5969 static void test_autoscroll(void)
5971 HWND hwnd = new_richedit(NULL);
5972 int lines, ret, redraw;
5973 POINT pt;
5975 for (redraw = 0; redraw <= 1; redraw++) {
5976 trace("testing with WM_SETREDRAW=%d\n", redraw);
5977 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
5978 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
5979 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5980 ok(lines == 8, "%d lines instead of 8\n", lines);
5981 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5982 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5983 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
5984 ret = GetWindowLong(hwnd, GWL_STYLE);
5985 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
5987 SendMessage(hwnd, WM_SETTEXT, 0, 0);
5988 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5989 ok(lines == 1, "%d lines instead of 1\n", lines);
5990 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5991 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5992 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
5993 ret = GetWindowLong(hwnd, GWL_STYLE);
5994 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
5997 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5998 DestroyWindow(hwnd);
6000 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6001 * auto vertical/horizontal scrolling options. */
6002 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6003 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6004 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6005 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6006 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6007 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6008 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6009 ret = GetWindowLong(hwnd, GWL_STYLE);
6010 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6011 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6012 DestroyWindow(hwnd);
6014 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6015 WS_POPUP|ES_MULTILINE,
6016 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6017 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6018 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6019 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6020 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6021 ret = GetWindowLong(hwnd, GWL_STYLE);
6022 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6023 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6024 DestroyWindow(hwnd);
6028 static void test_format_rect(void)
6030 HWND hwnd;
6031 RECT rc, expected, clientRect;
6032 int n;
6033 DWORD options;
6035 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6036 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6037 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6038 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6040 GetClientRect(hwnd, &clientRect);
6042 expected = clientRect;
6043 expected.left += 1;
6044 expected.right -= 1;
6045 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6046 ok(rc.top == expected.top && rc.left == expected.left &&
6047 rc.bottom == expected.bottom && rc.right == expected.right,
6048 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6049 rc.top, rc.left, rc.bottom, rc.right,
6050 expected.top, expected.left, expected.bottom, expected.right);
6052 for (n = -3; n <= 3; n++)
6054 rc = clientRect;
6055 rc.top += n;
6056 rc.left += n;
6057 rc.bottom -= n;
6058 rc.right -= n;
6059 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6061 expected = rc;
6062 expected.top = max(0, rc.top);
6063 expected.left = max(0, rc.left);
6064 expected.bottom = min(clientRect.bottom, rc.bottom);
6065 expected.right = min(clientRect.right, rc.right);
6066 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6067 ok(rc.top == expected.top && rc.left == expected.left &&
6068 rc.bottom == expected.bottom && rc.right == expected.right,
6069 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6070 n, rc.top, rc.left, rc.bottom, rc.right,
6071 expected.top, expected.left, expected.bottom, expected.right);
6074 rc = clientRect;
6075 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6076 expected = clientRect;
6077 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6078 ok(rc.top == expected.top && rc.left == expected.left &&
6079 rc.bottom == expected.bottom && rc.right == expected.right,
6080 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6081 rc.top, rc.left, rc.bottom, rc.right,
6082 expected.top, expected.left, expected.bottom, expected.right);
6084 /* Adding the selectionbar adds the selectionbar width to the left side. */
6085 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6086 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6087 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6088 expected.left += 8; /* selection bar width */
6089 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6090 ok(rc.top == expected.top && rc.left == expected.left &&
6091 rc.bottom == expected.bottom && rc.right == expected.right,
6092 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6093 rc.top, rc.left, rc.bottom, rc.right,
6094 expected.top, expected.left, expected.bottom, expected.right);
6096 rc = clientRect;
6097 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6098 expected = clientRect;
6099 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6100 ok(rc.top == expected.top && rc.left == expected.left &&
6101 rc.bottom == expected.bottom && rc.right == expected.right,
6102 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6103 rc.top, rc.left, rc.bottom, rc.right,
6104 expected.top, expected.left, expected.bottom, expected.right);
6106 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6107 * even if the left side is already 0. */
6108 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6109 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6110 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6111 expected.left -= 8; /* selection bar width */
6112 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6113 ok(rc.top == expected.top && rc.left == expected.left &&
6114 rc.bottom == expected.bottom && rc.right == expected.right,
6115 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6116 rc.top, rc.left, rc.bottom, rc.right,
6117 expected.top, expected.left, expected.bottom, expected.right);
6119 /* Set the absolute value of the formatting rectangle. */
6120 rc = clientRect;
6121 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6122 expected = clientRect;
6123 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6124 ok(rc.top == expected.top && rc.left == expected.left &&
6125 rc.bottom == expected.bottom && rc.right == expected.right,
6126 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6127 n, rc.top, rc.left, rc.bottom, rc.right,
6128 expected.top, expected.left, expected.bottom, expected.right);
6130 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6131 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6132 * tests show that this isn't true. */
6133 rc.top = 15;
6134 rc.left = 15;
6135 rc.bottom = clientRect.bottom - 15;
6136 rc.right = clientRect.right - 15;
6137 expected = rc;
6138 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6139 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6140 ok(rc.top == expected.top && rc.left == expected.left &&
6141 rc.bottom == expected.bottom && rc.right == expected.right,
6142 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6143 rc.top, rc.left, rc.bottom, rc.right,
6144 expected.top, expected.left, expected.bottom, expected.right);
6146 /* For some reason it does not limit the values to the client rect with
6147 * a WPARAM value of 1. */
6148 rc.top = -15;
6149 rc.left = -15;
6150 rc.bottom = clientRect.bottom + 15;
6151 rc.right = clientRect.right + 15;
6152 expected = rc;
6153 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6154 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6155 ok(rc.top == expected.top && rc.left == expected.left &&
6156 rc.bottom == expected.bottom && rc.right == expected.right,
6157 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6158 rc.top, rc.left, rc.bottom, rc.right,
6159 expected.top, expected.left, expected.bottom, expected.right);
6161 DestroyWindow(hwnd);
6163 /* The extended window style affects the formatting rectangle. */
6164 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6165 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6166 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6167 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6169 GetClientRect(hwnd, &clientRect);
6171 expected = clientRect;
6172 expected.left += 1;
6173 expected.top += 1;
6174 expected.right -= 1;
6175 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6176 ok(rc.top == expected.top && rc.left == expected.left &&
6177 rc.bottom == expected.bottom && rc.right == expected.right,
6178 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6179 rc.top, rc.left, rc.bottom, rc.right,
6180 expected.top, expected.left, expected.bottom, expected.right);
6182 rc = clientRect;
6183 rc.top += 5;
6184 rc.left += 5;
6185 rc.bottom -= 5;
6186 rc.right -= 5;
6187 expected = rc;
6188 expected.top -= 1;
6189 expected.left -= 1;
6190 expected.right += 1;
6191 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6192 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6193 ok(rc.top == expected.top && rc.left == expected.left &&
6194 rc.bottom == expected.bottom && rc.right == expected.right,
6195 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6196 rc.top, rc.left, rc.bottom, rc.right,
6197 expected.top, expected.left, expected.bottom, expected.right);
6199 DestroyWindow(hwnd);
6202 static void test_WM_GETDLGCODE(void)
6204 HWND hwnd;
6205 UINT res, expected;
6206 MSG msg;
6208 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6210 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6211 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6212 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6213 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6214 msg.hwnd = hwnd;
6215 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6216 expected = expected | DLGC_WANTMESSAGE;
6217 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6218 res, expected);
6219 DestroyWindow(hwnd);
6221 msg.message = WM_KEYDOWN;
6222 msg.wParam = VK_RETURN;
6223 msg.lParam = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) | 0x0001;
6224 msg.pt.x = 0;
6225 msg.pt.y = 0;
6226 msg.time = GetTickCount();
6228 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6229 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6230 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6231 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6232 msg.hwnd = hwnd;
6233 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6234 expected = expected | DLGC_WANTMESSAGE;
6235 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6236 res, expected);
6237 DestroyWindow(hwnd);
6239 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6240 ES_MULTILINE|WS_POPUP,
6241 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6242 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6243 msg.hwnd = hwnd;
6244 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6245 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6246 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6247 res, expected);
6248 DestroyWindow(hwnd);
6250 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6251 ES_WANTRETURN|WS_POPUP,
6252 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6253 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6254 msg.hwnd = hwnd;
6255 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6256 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6257 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6258 res, expected);
6259 DestroyWindow(hwnd);
6261 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6262 WS_POPUP,
6263 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6264 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6265 msg.hwnd = hwnd;
6266 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6267 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6268 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6269 res, expected);
6270 DestroyWindow(hwnd);
6272 msg.wParam = VK_TAB;
6273 msg.lParam = MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) | 0x0001;
6275 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6276 ES_MULTILINE|WS_POPUP,
6277 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6278 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6279 msg.hwnd = hwnd;
6280 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6281 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6282 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6283 res, expected);
6284 DestroyWindow(hwnd);
6286 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6287 WS_POPUP,
6288 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6289 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6290 msg.hwnd = hwnd;
6291 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6292 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6293 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6294 res, expected);
6295 DestroyWindow(hwnd);
6297 hold_key(VK_CONTROL);
6299 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6300 ES_MULTILINE|WS_POPUP,
6301 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6302 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6303 msg.hwnd = hwnd;
6304 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6305 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6306 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6307 res, expected);
6308 DestroyWindow(hwnd);
6310 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6311 WS_POPUP,
6312 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6313 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6314 msg.hwnd = hwnd;
6315 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6316 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6317 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6318 res, expected);
6319 DestroyWindow(hwnd);
6321 release_key(VK_CONTROL);
6323 msg.wParam = 'a';
6324 msg.lParam = MapVirtualKey('a', MAPVK_VK_TO_VSC) | 0x0001;
6326 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6327 ES_MULTILINE|WS_POPUP,
6328 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6329 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6330 msg.hwnd = hwnd;
6331 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6332 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6333 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6334 res, expected);
6335 DestroyWindow(hwnd);
6337 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6338 WS_POPUP,
6339 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6340 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6341 msg.hwnd = hwnd;
6342 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6343 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6344 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6345 res, expected);
6346 DestroyWindow(hwnd);
6348 msg.message = WM_CHAR;
6350 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6351 ES_MULTILINE|WS_POPUP,
6352 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6353 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6354 msg.hwnd = hwnd;
6355 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6356 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6357 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6358 res, expected);
6359 DestroyWindow(hwnd);
6361 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6362 WS_POPUP,
6363 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6364 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6365 msg.hwnd = hwnd;
6366 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6367 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6368 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6369 res, expected);
6370 DestroyWindow(hwnd);
6373 static void test_zoom(void)
6375 HWND hwnd;
6376 UINT ret;
6377 RECT rc;
6378 POINT pt;
6379 int numerator, denominator;
6381 hwnd = new_richedit(NULL);
6382 GetClientRect(hwnd, &rc);
6383 pt.x = (rc.right - rc.left) / 2;
6384 pt.y = (rc.bottom - rc.top) / 2;
6385 ClientToScreen(hwnd, &pt);
6387 /* Test initial zoom value */
6388 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6389 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6390 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6391 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6393 /* test scroll wheel */
6394 hold_key(VK_CONTROL);
6395 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6396 MAKELPARAM(pt.x, pt.y));
6397 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6398 release_key(VK_CONTROL);
6400 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6401 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6402 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6403 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6405 /* Test how much the mouse wheel can zoom in and out. */
6406 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)490, (LPARAM)100);
6407 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6409 hold_key(VK_CONTROL);
6410 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6411 MAKELPARAM(pt.x, pt.y));
6412 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6413 release_key(VK_CONTROL);
6415 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6416 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6417 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6418 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6420 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)491, (LPARAM)100);
6421 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6423 hold_key(VK_CONTROL);
6424 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6425 MAKELPARAM(pt.x, pt.y));
6426 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6427 release_key(VK_CONTROL);
6429 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6430 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6431 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6432 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6434 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)20, (LPARAM)100);
6435 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6437 hold_key(VK_CONTROL);
6438 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6439 MAKELPARAM(pt.x, pt.y));
6440 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6441 release_key(VK_CONTROL);
6443 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6444 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6445 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6446 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6448 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)19, (LPARAM)100);
6449 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6451 hold_key(VK_CONTROL);
6452 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6453 MAKELPARAM(pt.x, pt.y));
6454 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6455 release_key(VK_CONTROL);
6457 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6458 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6459 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6460 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6462 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6463 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)50, (LPARAM)13);
6464 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6466 hold_key(VK_CONTROL);
6467 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6468 MAKELPARAM(pt.x, pt.y));
6469 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6470 release_key(VK_CONTROL);
6472 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6473 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6474 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6475 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6477 /* Test bounds checking on EM_SETZOOM */
6478 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)127);
6479 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6481 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)127, (LPARAM)2);
6482 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6484 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)128);
6485 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6487 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6488 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6489 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6490 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6492 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)128, (LPARAM)2);
6493 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6495 /* See if negative numbers are accepted. */
6496 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)-100, (LPARAM)-100);
6497 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6499 /* See if negative numbers are accepted. */
6500 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)100);
6501 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6503 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6504 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6505 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6506 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6508 /* Reset the zoom value */
6509 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)0);
6510 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6512 DestroyWindow(hwnd);
6515 START_TEST( editor )
6517 /* Must explicitly LoadLibrary(). The test has no references to functions in
6518 * RICHED20.DLL, so the linker doesn't actually link to it. */
6519 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
6520 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
6522 is_win9x = GetVersion() & 0x80000000;
6524 test_WM_CHAR();
6525 test_EM_FINDTEXT();
6526 test_EM_GETLINE();
6527 test_EM_POSFROMCHAR();
6528 test_EM_SCROLLCARET();
6529 test_EM_SCROLL();
6530 test_scrollbar_visibility();
6531 test_WM_SETTEXT();
6532 test_EM_LINELENGTH();
6533 test_EM_SETCHARFORMAT();
6534 test_EM_SETTEXTMODE();
6535 test_TM_PLAINTEXT();
6536 test_EM_SETOPTIONS();
6537 test_WM_GETTEXT();
6538 test_EM_GETTEXTRANGE();
6539 test_EM_GETSELTEXT();
6540 test_EM_SETUNDOLIMIT();
6541 test_ES_PASSWORD();
6542 test_EM_SETTEXTEX();
6543 test_EM_LIMITTEXT();
6544 test_EM_EXLIMITTEXT();
6545 test_EM_GETLIMITTEXT();
6546 test_WM_SETFONT();
6547 test_EM_GETMODIFY();
6548 test_EM_EXSETSEL();
6549 test_WM_PASTE();
6550 test_EM_STREAMIN();
6551 test_EM_STREAMOUT();
6552 test_EM_StreamIn_Undo();
6553 test_EM_FORMATRANGE();
6554 test_unicode_conversions();
6555 test_EM_GETTEXTLENGTHEX();
6556 test_EM_REPLACESEL(1);
6557 test_EM_REPLACESEL(0);
6558 test_WM_NOTIFY();
6559 test_EM_AUTOURLDETECT();
6560 test_eventMask();
6561 test_undo_coalescing();
6562 test_word_movement();
6563 test_EM_CHARFROMPOS();
6564 test_SETPARAFORMAT();
6565 test_word_wrap();
6566 test_autoscroll();
6567 test_format_rect();
6568 test_WM_GETDLGCODE();
6569 test_zoom();
6571 /* Set the environment variable WINETEST_RICHED20 to keep windows
6572 * responsive and open for 30 seconds. This is useful for debugging.
6574 if (getenv( "WINETEST_RICHED20" )) {
6575 keep_responsive(30);
6578 OleFlushClipboard();
6579 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());