push 556f62bab943c37bb6cbad95a6ecb3d73e04a93f
[wine/hacks.git] / dlls / riched20 / tests / editor.c
blob78a9541ca055c53824b5fc775993827b80cbbaab
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 /* cpMax of text length is used instead of -1 in this case */
1424 textRange.lpstrText = buffer;
1425 textRange.chrg.cpMin = 0;
1426 textRange.chrg.cpMax = -1;
1427 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1428 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1429 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1431 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1432 textRange.lpstrText = buffer;
1433 textRange.chrg.cpMin = -1;
1434 textRange.chrg.cpMax = 1;
1435 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1436 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1437 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1439 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1440 textRange.lpstrText = buffer;
1441 textRange.chrg.cpMin = 1;
1442 textRange.chrg.cpMax = -1;
1443 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1444 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1445 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1447 /* no end character is copied if cpMax - cpMin < 0 */
1448 textRange.lpstrText = buffer;
1449 textRange.chrg.cpMin = 5;
1450 textRange.chrg.cpMax = 5;
1451 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1452 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1453 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1455 /* cpMax of text length is used if cpMax > text length*/
1456 textRange.lpstrText = buffer;
1457 textRange.chrg.cpMin = 0;
1458 textRange.chrg.cpMax = 1000;
1459 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1460 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1461 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1463 DestroyWindow(hwndRichEdit);
1466 static void test_EM_GETSELTEXT(void)
1468 HWND hwndRichEdit = new_richedit(NULL);
1469 const char * text1 = "foo bar\r\nfoo bar";
1470 const char * text2 = "foo bar\rfoo bar";
1471 const char * expect = "bar\rfoo";
1472 char buffer[1024] = {0};
1473 LRESULT result;
1475 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1477 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1478 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1479 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1480 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1482 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1484 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1485 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1486 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1487 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1489 DestroyWindow(hwndRichEdit);
1492 /* FIXME: need to test unimplemented options and robustly test wparam */
1493 static void test_EM_SETOPTIONS(void)
1495 HWND hwndRichEdit;
1496 static const char text[] = "Hello. My name is RichEdit!";
1497 char buffer[1024] = {0};
1498 DWORD dwStyle, options, oldOptions;
1499 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1500 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1501 ES_SELECTIONBAR|ES_VERTICAL;
1503 /* Test initial options. */
1504 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1505 0, 0, 200, 60, NULL, NULL,
1506 hmoduleRichEdit, NULL);
1507 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1508 RICHEDIT_CLASS, (int) GetLastError());
1509 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1510 ok(options == 0, "Incorrect initial options %x\n", options);
1511 DestroyWindow(hwndRichEdit);
1513 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1514 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1515 0, 0, 200, 60, NULL, NULL,
1516 hmoduleRichEdit, NULL);
1517 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1518 RICHEDIT_CLASS, (int) GetLastError());
1519 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1520 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1521 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1522 "Incorrect initial options %x\n", options);
1524 /* NEGATIVE TESTING - NO OPTIONS SET */
1525 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1526 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1528 /* testing no readonly by sending 'a' to the control*/
1529 SetFocus(hwndRichEdit);
1530 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1531 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1532 ok(buffer[0]=='a',
1533 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1534 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1536 /* READONLY - sending 'a' to the control */
1537 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1538 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1539 SetFocus(hwndRichEdit);
1540 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1541 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1542 ok(buffer[0]==text[0],
1543 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1545 /* EM_SETOPTIONS changes the window style, but changing the
1546 * window style does not change the options. */
1547 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1548 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1549 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1550 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1551 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1552 /* Confirm that the text is still read only. */
1553 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1554 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1555 ok(buffer[0]==text[0],
1556 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1558 oldOptions = options;
1559 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1560 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1561 ok(options == oldOptions,
1562 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1564 DestroyWindow(hwndRichEdit);
1567 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1569 CHARFORMAT2W text_format;
1570 text_format.cbSize = sizeof(text_format);
1571 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1572 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1573 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1576 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1578 int link_present = 0;
1580 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1581 if (is_url)
1582 { /* control text is url; should get CFE_LINK */
1583 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1585 else
1587 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1591 static HWND new_static_wnd(HWND parent) {
1592 return new_window("Static", 0, parent);
1595 static void test_EM_AUTOURLDETECT(void)
1597 /* DO NOT change the properties of the first two elements. To shorten the
1598 tests, all tests after WM_SETTEXT test just the first two elements -
1599 one non-URL and one URL */
1600 struct urls_s {
1601 const char *text;
1602 int is_url;
1603 } urls[12] = {
1604 {"winehq.org", 0},
1605 {"http://www.winehq.org", 1},
1606 {"http//winehq.org", 0},
1607 {"ww.winehq.org", 0},
1608 {"www.winehq.org", 1},
1609 {"ftp://192.168.1.1", 1},
1610 {"ftp//192.168.1.1", 0},
1611 {"mailto:your@email.com", 1},
1612 {"prospero:prosperoserver", 1},
1613 {"telnet:test", 1},
1614 {"news:newserver", 1},
1615 {"wais:waisserver", 1}
1618 int i, j;
1619 int urlRet=-1;
1620 HWND hwndRichEdit, parent;
1622 /* All of the following should cause the URL to be detected */
1623 const char * templates_delim[] = {
1624 "This is some text with X on it",
1625 "This is some text with (X) on it",
1626 "This is some text with X\r on it",
1627 "This is some text with ---X--- on it",
1628 "This is some text with \"X\" on it",
1629 "This is some text with 'X' on it",
1630 "This is some text with 'X' on it",
1631 "This is some text with :X: on it",
1633 "This text ends with X",
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",
1639 "This is some text with X: on it",
1641 "This is some text with (X on it",
1642 "This is some text with \rX on it",
1643 "This is some text with ---X on it",
1644 "This is some text with \"X on it",
1645 "This is some text with 'X on it",
1646 "This is some text with :X on it",
1648 /* None of these should cause the URL to be detected */
1649 const char * templates_non_delim[] = {
1650 "This is some text with |X| on it",
1651 "This is some text with *X* on it",
1652 "This is some text with /X/ on it",
1653 "This is some text with +X+ on it",
1654 "This is some text with %X% on it",
1655 "This is some text with #X# on it",
1656 "This is some text with @X@ on it",
1657 "This is some text with \\X\\ on it",
1658 "This is some text with |X on it",
1659 "This is some text with *X on it",
1660 "This is some text with /X on it",
1661 "This is some text with +X on it",
1662 "This is some text with %X on it",
1663 "This is some text with #X on it",
1664 "This is some text with @X on it",
1665 "This is some text with \\X on it",
1667 /* All of these cause the URL detection to be extended by one more byte,
1668 thus demonstrating that the tested character is considered as part
1669 of the URL. */
1670 const char * templates_xten_delim[] = {
1671 "This is some text with X| on it",
1672 "This is some text with X* on it",
1673 "This is some text with X/ on it",
1674 "This is some text with X+ on it",
1675 "This is some text with X% on it",
1676 "This is some text with X# on it",
1677 "This is some text with X@ on it",
1678 "This is some text with X\\ on it",
1680 char buffer[1024];
1682 parent = new_static_wnd(NULL);
1683 hwndRichEdit = new_richedit(parent);
1684 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1685 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1686 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1687 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1688 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1689 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1690 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1691 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1692 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1693 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1694 /* for each url, check the text to see if CFE_LINK effect is present */
1695 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1697 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1698 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1699 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1701 /* Link detection should happen immediately upon WM_SETTEXT */
1702 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1703 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1704 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1706 DestroyWindow(hwndRichEdit);
1708 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1709 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1710 hwndRichEdit = new_richedit(parent);
1712 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1713 char * at_pos;
1714 int at_offset;
1715 int end_offset;
1717 at_pos = strchr(templates_delim[j], 'X');
1718 at_offset = at_pos - templates_delim[j];
1719 strncpy(buffer, templates_delim[j], at_offset);
1720 buffer[at_offset] = '\0';
1721 strcat(buffer, urls[i].text);
1722 strcat(buffer, templates_delim[j] + at_offset + 1);
1723 end_offset = at_offset + strlen(urls[i].text);
1725 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1726 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1728 /* This assumes no templates start with the URL itself, and that they
1729 have at least two characters before the URL text */
1730 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1731 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1732 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1733 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1734 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1735 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1737 if (urls[i].is_url)
1739 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1740 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1741 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1742 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1744 else
1746 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1747 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1748 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1749 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1751 if (buffer[end_offset] != '\0')
1753 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1754 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1755 if (buffer[end_offset +1] != '\0')
1757 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1758 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1763 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1764 char * at_pos;
1765 int at_offset;
1766 int end_offset;
1768 at_pos = strchr(templates_non_delim[j], 'X');
1769 at_offset = at_pos - templates_non_delim[j];
1770 strncpy(buffer, templates_non_delim[j], at_offset);
1771 buffer[at_offset] = '\0';
1772 strcat(buffer, urls[i].text);
1773 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1774 end_offset = at_offset + strlen(urls[i].text);
1776 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1777 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1779 /* This assumes no templates start with the URL itself, and that they
1780 have at least two characters before the URL text */
1781 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1782 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1783 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1784 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1785 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1786 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1788 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1789 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1790 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1791 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1792 if (buffer[end_offset] != '\0')
1794 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1795 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1796 if (buffer[end_offset +1] != '\0')
1798 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1799 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1804 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1805 char * at_pos;
1806 int at_offset;
1807 int end_offset;
1809 at_pos = strchr(templates_xten_delim[j], 'X');
1810 at_offset = at_pos - templates_xten_delim[j];
1811 strncpy(buffer, templates_xten_delim[j], at_offset);
1812 buffer[at_offset] = '\0';
1813 strcat(buffer, urls[i].text);
1814 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1815 end_offset = at_offset + strlen(urls[i].text);
1817 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1818 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1820 /* This assumes no templates start with the URL itself, and that they
1821 have at least two characters before the URL text */
1822 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1823 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1824 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1825 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1826 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1827 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1829 if (urls[i].is_url)
1831 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1832 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1833 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1834 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1835 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1836 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1838 else
1840 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1841 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1842 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1843 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1844 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1845 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1847 if (buffer[end_offset +1] != '\0')
1849 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1850 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1851 if (buffer[end_offset +2] != '\0')
1853 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1854 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1859 DestroyWindow(hwndRichEdit);
1860 hwndRichEdit = NULL;
1863 /* Test detection of URLs within normal text - WM_CHAR case. */
1864 /* Test only the first two URL examples for brevity */
1865 for (i = 0; i < 2; i++) {
1866 hwndRichEdit = new_richedit(parent);
1868 /* Also for brevity, test only the first three delimiters */
1869 for (j = 0; j < 3; j++) {
1870 char * at_pos;
1871 int at_offset;
1872 int end_offset;
1873 int u, v;
1875 at_pos = strchr(templates_delim[j], 'X');
1876 at_offset = at_pos - templates_delim[j];
1877 end_offset = at_offset + strlen(urls[i].text);
1879 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1880 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1881 for (u = 0; templates_delim[j][u]; u++) {
1882 if (templates_delim[j][u] == '\r') {
1883 simulate_typing_characters(hwndRichEdit, "\r");
1884 } else if (templates_delim[j][u] != 'X') {
1885 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1886 } else {
1887 for (v = 0; urls[i].text[v]; v++) {
1888 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1892 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1894 /* This assumes no templates start with the URL itself, and that they
1895 have at least two characters before the URL text */
1896 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1897 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1898 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1899 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1900 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1901 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1903 if (urls[i].is_url)
1905 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1906 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1907 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1908 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1910 else
1912 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1913 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1914 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1915 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1917 if (buffer[end_offset] != '\0')
1919 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1920 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1921 if (buffer[end_offset +1] != '\0')
1923 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1924 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1928 /* The following will insert a paragraph break after the first character
1929 of the URL candidate, thus breaking the URL. It is expected that the
1930 CFE_LINK attribute should break across both pieces of the URL */
1931 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1932 simulate_typing_characters(hwndRichEdit, "\r");
1933 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1935 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1936 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1937 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1938 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1939 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1940 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1942 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1943 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1944 /* end_offset moved because of paragraph break */
1945 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1946 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1947 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1948 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1950 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1951 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1952 if (buffer[end_offset +2] != '\0')
1954 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1955 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1959 /* The following will remove the just-inserted paragraph break, thus
1960 restoring the URL */
1961 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1962 simulate_typing_characters(hwndRichEdit, "\b");
1963 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1965 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1966 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1967 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1968 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1969 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1970 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1972 if (urls[i].is_url)
1974 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1975 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1976 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1977 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1979 else
1981 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1982 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1983 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1984 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1986 if (buffer[end_offset] != '\0')
1988 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1989 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1990 if (buffer[end_offset +1] != '\0')
1992 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1993 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1997 DestroyWindow(hwndRichEdit);
1998 hwndRichEdit = NULL;
2001 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2002 /* Test just the first two URL examples for brevity */
2003 for (i = 0; i < 2; i++) {
2004 SETTEXTEX st;
2006 hwndRichEdit = new_richedit(parent);
2008 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2009 be detected:
2010 1) Set entire text, a la WM_SETTEXT
2011 2) Set a selection of the text to the URL
2012 3) Set a portion of the text at a time, which eventually results in
2013 an URL
2014 All of them should give equivalent results
2017 /* Set entire text in one go, like WM_SETTEXT */
2018 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2019 char * at_pos;
2020 int at_offset;
2021 int end_offset;
2023 st.codepage = CP_ACP;
2024 st.flags = ST_DEFAULT;
2026 at_pos = strchr(templates_delim[j], 'X');
2027 at_offset = at_pos - templates_delim[j];
2028 strncpy(buffer, templates_delim[j], at_offset);
2029 buffer[at_offset] = '\0';
2030 strcat(buffer, urls[i].text);
2031 strcat(buffer, templates_delim[j] + at_offset + 1);
2032 end_offset = at_offset + strlen(urls[i].text);
2034 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2035 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2037 /* This assumes no templates start with the URL itself, and that they
2038 have at least two characters before the URL text */
2039 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2040 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2041 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2042 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2043 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2044 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2046 if (urls[i].is_url)
2048 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2049 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2050 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2051 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2053 else
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2057 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2058 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2060 if (buffer[end_offset] != '\0')
2062 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2063 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2064 if (buffer[end_offset +1] != '\0')
2066 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2067 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2072 /* Set selection with X to the URL */
2073 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2074 char * at_pos;
2075 int at_offset;
2076 int end_offset;
2078 at_pos = strchr(templates_delim[j], 'X');
2079 at_offset = at_pos - templates_delim[j];
2080 end_offset = at_offset + strlen(urls[i].text);
2082 st.codepage = CP_ACP;
2083 st.flags = ST_DEFAULT;
2084 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2085 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2086 st.flags = ST_SELECTION;
2087 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2088 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2089 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2091 /* This assumes no templates start with the URL itself, and that they
2092 have at least two characters before the URL text */
2093 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2094 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2095 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2096 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2097 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2098 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2100 if (urls[i].is_url)
2102 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2103 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2104 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2105 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2107 else
2109 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2110 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2111 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2112 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2114 if (buffer[end_offset] != '\0')
2116 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2117 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2118 if (buffer[end_offset +1] != '\0')
2120 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2121 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2126 /* Set selection with X to the first character of the URL, then the rest */
2127 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2128 char * at_pos;
2129 int at_offset;
2130 int end_offset;
2132 at_pos = strchr(templates_delim[j], 'X');
2133 at_offset = at_pos - templates_delim[j];
2134 end_offset = at_offset + strlen(urls[i].text);
2136 strcpy(buffer, "YY");
2137 buffer[0] = urls[i].text[0];
2139 st.codepage = CP_ACP;
2140 st.flags = ST_DEFAULT;
2141 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2142 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2143 st.flags = ST_SELECTION;
2144 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2145 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2146 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2147 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2148 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2150 /* This assumes no templates start with the URL itself, and that they
2151 have at least two characters before the URL text */
2152 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2153 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2154 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2155 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2156 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2157 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2159 if (urls[i].is_url)
2161 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2162 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2163 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2164 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2166 else
2168 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2169 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2170 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2171 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2173 if (buffer[end_offset] != '\0')
2175 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2176 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2177 if (buffer[end_offset +1] != '\0')
2179 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2180 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2185 DestroyWindow(hwndRichEdit);
2186 hwndRichEdit = NULL;
2189 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2190 /* Test just the first two URL examples for brevity */
2191 for (i = 0; i < 2; i++) {
2192 hwndRichEdit = new_richedit(parent);
2194 /* Set selection with X to the URL */
2195 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2196 char * at_pos;
2197 int at_offset;
2198 int end_offset;
2200 at_pos = strchr(templates_delim[j], 'X');
2201 at_offset = at_pos - templates_delim[j];
2202 end_offset = at_offset + strlen(urls[i].text);
2204 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2205 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2206 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2207 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2208 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2210 /* This assumes no templates start with the URL itself, and that they
2211 have at least two characters before the URL text */
2212 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2213 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2214 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2215 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2216 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2217 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2219 if (urls[i].is_url)
2221 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2222 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2223 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2224 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2226 else
2228 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2229 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2230 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2231 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2233 if (buffer[end_offset] != '\0')
2235 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2236 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2237 if (buffer[end_offset +1] != '\0')
2239 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2240 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2245 /* Set selection with X to the first character of the URL, then the rest */
2246 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2247 char * at_pos;
2248 int at_offset;
2249 int end_offset;
2251 at_pos = strchr(templates_delim[j], 'X');
2252 at_offset = at_pos - templates_delim[j];
2253 end_offset = at_offset + strlen(urls[i].text);
2255 strcpy(buffer, "YY");
2256 buffer[0] = urls[i].text[0];
2258 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2259 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2260 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2261 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2262 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2263 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2264 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2266 /* This assumes no templates start with the URL itself, and that they
2267 have at least two characters before the URL text */
2268 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2269 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2270 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2271 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2272 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2273 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2275 if (urls[i].is_url)
2277 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2278 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2279 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2280 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2282 else
2284 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2285 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2286 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2287 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2289 if (buffer[end_offset] != '\0')
2291 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2292 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2293 if (buffer[end_offset +1] != '\0')
2295 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2296 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2301 DestroyWindow(hwndRichEdit);
2302 hwndRichEdit = NULL;
2305 DestroyWindow(parent);
2308 static void test_EM_SCROLL(void)
2310 int i, j;
2311 int r; /* return value */
2312 int expr; /* expected return value */
2313 HWND hwndRichEdit = new_richedit(NULL);
2314 int y_before, y_after; /* units of lines of text */
2316 /* test a richedit box containing a single line of text */
2317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2318 expr = 0x00010000;
2319 for (i = 0; i < 4; i++) {
2320 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2322 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2323 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2324 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2325 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2326 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2327 "(i == %d)\n", y_after, i);
2331 * test a richedit box that will scroll. There are two general
2332 * cases: the case without any long lines and the case with a long
2333 * line.
2335 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2336 if (i == 0)
2337 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2338 else
2339 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2340 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2341 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2342 "LONG LINE \nb\nc\nd\ne");
2343 for (j = 0; j < 12; j++) /* reset scroll position to top */
2344 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2346 /* get first visible line */
2347 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2348 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2350 /* get new current first visible line */
2351 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2353 ok(((r & 0xffffff00) == 0x00010000) &&
2354 ((r & 0x000000ff) != 0x00000000),
2355 "EM_SCROLL page down didn't scroll by a small positive number of "
2356 "lines (r == 0x%08x)\n", r);
2357 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2358 "(line %d scrolled to line %d\n", y_before, y_after);
2360 y_before = y_after;
2362 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2363 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2364 ok(((r & 0xffffff00) == 0x0001ff00),
2365 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2366 "(r == 0x%08x)\n", r);
2367 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2368 "%d scrolled to line %d\n", y_before, y_after);
2370 y_before = y_after;
2372 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2374 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2376 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2377 "(r == 0x%08x)\n", r);
2378 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2379 "1 line (%d scrolled to %d)\n", y_before, y_after);
2381 y_before = y_after;
2383 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2385 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2387 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2388 "(r == 0x%08x)\n", r);
2389 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2390 "line (%d scrolled to %d)\n", y_before, y_after);
2392 y_before = y_after;
2394 r = SendMessage(hwndRichEdit, EM_SCROLL,
2395 SB_LINEUP, 0); /* lineup beyond top */
2397 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2399 ok(r == 0x00010000,
2400 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2401 ok(y_before == y_after,
2402 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2404 y_before = y_after;
2406 r = SendMessage(hwndRichEdit, EM_SCROLL,
2407 SB_PAGEUP, 0);/*page up beyond top */
2409 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2411 ok(r == 0x00010000,
2412 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2413 ok(y_before == y_after,
2414 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2416 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2417 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2418 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2419 r = SendMessage(hwndRichEdit, EM_SCROLL,
2420 SB_PAGEDOWN, 0); /* page down beyond bot */
2421 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2423 ok(r == 0x00010000,
2424 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2425 ok(y_before == y_after,
2426 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2427 y_before, y_after);
2429 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2430 SendMessage(hwndRichEdit, EM_SCROLL,
2431 SB_LINEDOWN, 0); /* line down beyond bot */
2432 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2434 ok(r == 0x00010000,
2435 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2436 ok(y_before == y_after,
2437 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2438 y_before, y_after);
2440 DestroyWindow(hwndRichEdit);
2443 unsigned int recursionLevel = 0;
2444 unsigned int WM_SIZE_recursionLevel = 0;
2445 BOOL bailedOutOfRecursion = FALSE;
2446 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2448 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2450 LRESULT r;
2452 if (bailedOutOfRecursion) return 0;
2453 if (recursionLevel >= 32) {
2454 bailedOutOfRecursion = TRUE;
2455 return 0;
2458 recursionLevel++;
2459 switch (message) {
2460 case WM_SIZE:
2461 WM_SIZE_recursionLevel++;
2462 r = richeditProc(hwnd, message, wParam, lParam);
2463 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2464 ShowScrollBar(hwnd, SB_VERT, TRUE);
2465 WM_SIZE_recursionLevel--;
2466 break;
2467 default:
2468 r = richeditProc(hwnd, message, wParam, lParam);
2469 break;
2471 recursionLevel--;
2472 return r;
2475 static void test_scrollbar_visibility(void)
2477 HWND hwndRichEdit;
2478 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2479 SCROLLINFO si;
2480 WNDCLASSA cls;
2481 BOOL r;
2483 /* These tests show that richedit should temporarily refrain from automatically
2484 hiding or showing its scrollbars (vertical at least) when an explicit request
2485 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2486 Some applications depend on forced showing (when otherwise richedit would
2487 hide the vertical scrollbar) and are thrown on an endless recursive loop
2488 if richedit auto-hides the scrollbar again. Apparently they never heard of
2489 the ES_DISABLENOSCROLL style... */
2491 hwndRichEdit = new_richedit(NULL);
2493 /* Test default scrollbar visibility behavior */
2494 memset(&si, 0, sizeof(si));
2495 si.cbSize = sizeof(si);
2496 si.fMask = SIF_PAGE | SIF_RANGE;
2497 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2498 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2499 "Vertical scrollbar is visible, should be invisible.\n");
2500 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2501 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2502 si.nPage, si.nMin, si.nMax);
2504 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2505 memset(&si, 0, sizeof(si));
2506 si.cbSize = sizeof(si);
2507 si.fMask = SIF_PAGE | SIF_RANGE;
2508 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2509 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2510 "Vertical scrollbar is visible, should be invisible.\n");
2511 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2512 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2513 si.nPage, si.nMin, si.nMax);
2515 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2516 memset(&si, 0, sizeof(si));
2517 si.cbSize = sizeof(si);
2518 si.fMask = SIF_PAGE | SIF_RANGE;
2519 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2520 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2521 "Vertical scrollbar is invisible, should be visible.\n");
2522 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2523 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2524 si.nPage, si.nMin, si.nMax);
2526 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2527 even though it hides the scrollbar */
2528 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2529 memset(&si, 0, sizeof(si));
2530 si.cbSize = sizeof(si);
2531 si.fMask = SIF_PAGE | SIF_RANGE;
2532 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2533 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2534 "Vertical scrollbar is visible, should be invisible.\n");
2535 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2536 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2537 si.nPage, si.nMin, si.nMax);
2539 /* Setting non-scrolling text again does *not* reset scrollbar range */
2540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2541 memset(&si, 0, sizeof(si));
2542 si.cbSize = sizeof(si);
2543 si.fMask = SIF_PAGE | SIF_RANGE;
2544 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2545 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2546 "Vertical scrollbar is visible, should be invisible.\n");
2547 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2548 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2549 si.nPage, si.nMin, si.nMax);
2551 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2552 memset(&si, 0, sizeof(si));
2553 si.cbSize = sizeof(si);
2554 si.fMask = SIF_PAGE | SIF_RANGE;
2555 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2556 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2557 "Vertical scrollbar is visible, should be invisible.\n");
2558 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2559 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2560 si.nPage, si.nMin, si.nMax);
2562 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2563 memset(&si, 0, sizeof(si));
2564 si.cbSize = sizeof(si);
2565 si.fMask = SIF_PAGE | SIF_RANGE;
2566 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2567 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2568 "Vertical scrollbar is visible, should be invisible.\n");
2569 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2570 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2571 si.nPage, si.nMin, si.nMax);
2573 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2574 memset(&si, 0, sizeof(si));
2575 si.cbSize = sizeof(si);
2576 si.fMask = SIF_PAGE | SIF_RANGE;
2577 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2578 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2579 "Vertical scrollbar is visible, should be invisible.\n");
2580 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2581 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2582 si.nPage, si.nMin, si.nMax);
2584 DestroyWindow(hwndRichEdit);
2586 /* Test again, with ES_DISABLENOSCROLL style */
2587 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2589 /* Test default scrollbar visibility behavior */
2590 memset(&si, 0, sizeof(si));
2591 si.cbSize = sizeof(si);
2592 si.fMask = SIF_PAGE | SIF_RANGE;
2593 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2594 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2595 "Vertical scrollbar is invisible, should be visible.\n");
2596 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2597 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2598 si.nPage, si.nMin, si.nMax);
2600 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2601 memset(&si, 0, sizeof(si));
2602 si.cbSize = sizeof(si);
2603 si.fMask = SIF_PAGE | SIF_RANGE;
2604 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2605 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2606 "Vertical scrollbar is invisible, should be visible.\n");
2607 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2608 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2609 si.nPage, si.nMin, si.nMax);
2611 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2612 memset(&si, 0, sizeof(si));
2613 si.cbSize = sizeof(si);
2614 si.fMask = SIF_PAGE | SIF_RANGE;
2615 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2616 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2617 "Vertical scrollbar is invisible, should be visible.\n");
2618 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2619 "reported page/range is %d (%d..%d)\n",
2620 si.nPage, si.nMin, si.nMax);
2622 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2623 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2624 memset(&si, 0, sizeof(si));
2625 si.cbSize = sizeof(si);
2626 si.fMask = SIF_PAGE | SIF_RANGE;
2627 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2628 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2629 "Vertical scrollbar is invisible, should be visible.\n");
2630 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2631 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2632 si.nPage, si.nMin, si.nMax);
2634 /* Setting non-scrolling text again does *not* reset scrollbar range */
2635 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2636 memset(&si, 0, sizeof(si));
2637 si.cbSize = sizeof(si);
2638 si.fMask = SIF_PAGE | SIF_RANGE;
2639 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2640 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2641 "Vertical scrollbar is invisible, should be visible.\n");
2642 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2643 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2644 si.nPage, si.nMin, si.nMax);
2646 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2647 memset(&si, 0, sizeof(si));
2648 si.cbSize = sizeof(si);
2649 si.fMask = SIF_PAGE | SIF_RANGE;
2650 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2651 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2652 "Vertical scrollbar is invisible, should be visible.\n");
2653 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2654 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2655 si.nPage, si.nMin, si.nMax);
2657 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2658 memset(&si, 0, sizeof(si));
2659 si.cbSize = sizeof(si);
2660 si.fMask = SIF_PAGE | SIF_RANGE;
2661 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2662 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2663 "Vertical scrollbar is invisible, should be visible.\n");
2664 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2665 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2666 si.nPage, si.nMin, si.nMax);
2668 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2669 memset(&si, 0, sizeof(si));
2670 si.cbSize = sizeof(si);
2671 si.fMask = SIF_PAGE | SIF_RANGE;
2672 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2673 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2674 "Vertical scrollbar is invisible, should be visible.\n");
2675 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2676 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2677 si.nPage, si.nMin, si.nMax);
2679 DestroyWindow(hwndRichEdit);
2681 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2682 hwndRichEdit = new_richedit(NULL);
2684 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2685 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2686 memset(&si, 0, sizeof(si));
2687 si.cbSize = sizeof(si);
2688 si.fMask = SIF_PAGE | SIF_RANGE;
2689 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2690 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2691 "Vertical scrollbar is invisible, should be visible.\n");
2692 todo_wine {
2693 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2694 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2695 si.nPage, si.nMin, si.nMax);
2698 /* Ditto, see above */
2699 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2700 memset(&si, 0, sizeof(si));
2701 si.cbSize = sizeof(si);
2702 si.fMask = SIF_PAGE | SIF_RANGE;
2703 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2704 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2705 "Vertical scrollbar is invisible, should be visible.\n");
2706 todo_wine {
2707 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2708 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2709 si.nPage, si.nMin, si.nMax);
2712 /* Ditto, see above */
2713 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2714 memset(&si, 0, sizeof(si));
2715 si.cbSize = sizeof(si);
2716 si.fMask = SIF_PAGE | SIF_RANGE;
2717 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2718 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2719 "Vertical scrollbar is invisible, should be visible.\n");
2720 todo_wine {
2721 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2722 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2723 si.nPage, si.nMin, si.nMax);
2726 /* Ditto, see above */
2727 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2728 memset(&si, 0, sizeof(si));
2729 si.cbSize = sizeof(si);
2730 si.fMask = SIF_PAGE | SIF_RANGE;
2731 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2732 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2733 "Vertical scrollbar is invisible, should be visible.\n");
2734 todo_wine {
2735 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2736 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2737 si.nPage, si.nMin, si.nMax);
2740 /* Ditto, see above */
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 invisible, should be visible.\n");
2748 todo_wine {
2749 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2750 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2751 si.nPage, si.nMin, si.nMax);
2754 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2755 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2756 memset(&si, 0, sizeof(si));
2757 si.cbSize = sizeof(si);
2758 si.fMask = SIF_PAGE | SIF_RANGE;
2759 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2760 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2761 "Vertical scrollbar is visible, should be invisible.\n");
2762 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2763 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2764 si.nPage, si.nMin, si.nMax);
2766 DestroyWindow(hwndRichEdit);
2768 hwndRichEdit = new_richedit(NULL);
2770 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2771 memset(&si, 0, sizeof(si));
2772 si.cbSize = sizeof(si);
2773 si.fMask = SIF_PAGE | SIF_RANGE;
2774 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2775 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2776 "Vertical scrollbar is visible, should be invisible.\n");
2777 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2778 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2779 si.nPage, si.nMin, si.nMax);
2781 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2782 memset(&si, 0, sizeof(si));
2783 si.cbSize = sizeof(si);
2784 si.fMask = SIF_PAGE | SIF_RANGE;
2785 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2786 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2787 "Vertical scrollbar is visible, should be invisible.\n");
2788 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2789 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2790 si.nPage, si.nMin, si.nMax);
2792 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2793 memset(&si, 0, sizeof(si));
2794 si.cbSize = sizeof(si);
2795 si.fMask = SIF_PAGE | SIF_RANGE;
2796 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2797 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2798 "Vertical scrollbar is visible, should be invisible.\n");
2799 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2800 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2801 si.nPage, si.nMin, si.nMax);
2803 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2804 memset(&si, 0, sizeof(si));
2805 si.cbSize = sizeof(si);
2806 si.fMask = SIF_PAGE | SIF_RANGE;
2807 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2808 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2809 "Vertical scrollbar is visible, should be invisible.\n");
2810 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2811 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2812 si.nPage, si.nMin, si.nMax);
2814 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2815 memset(&si, 0, sizeof(si));
2816 si.cbSize = sizeof(si);
2817 si.fMask = SIF_PAGE | SIF_RANGE;
2818 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2819 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2820 "Vertical scrollbar is invisible, should be visible.\n");
2821 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2822 "reported page/range is %d (%d..%d)\n",
2823 si.nPage, si.nMin, si.nMax);
2825 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2826 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2827 memset(&si, 0, sizeof(si));
2828 si.cbSize = sizeof(si);
2829 si.fMask = SIF_PAGE | SIF_RANGE;
2830 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2832 "Vertical scrollbar is visible, should be invisible.\n");
2833 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2834 "reported page/range is %d (%d..%d)\n",
2835 si.nPage, si.nMin, si.nMax);
2837 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2838 memset(&si, 0, sizeof(si));
2839 si.cbSize = sizeof(si);
2840 si.fMask = SIF_PAGE | SIF_RANGE;
2841 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2842 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2843 "Vertical scrollbar is visible, should be invisible.\n");
2844 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2845 "reported page/range is %d (%d..%d)\n",
2846 si.nPage, si.nMin, si.nMax);
2848 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2849 EM_SCROLL will make visible any forcefully invisible scrollbar */
2850 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2851 memset(&si, 0, sizeof(si));
2852 si.cbSize = sizeof(si);
2853 si.fMask = SIF_PAGE | SIF_RANGE;
2854 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2855 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2856 "Vertical scrollbar is invisible, should be visible.\n");
2857 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2858 "reported page/range is %d (%d..%d)\n",
2859 si.nPage, si.nMin, si.nMax);
2861 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2862 memset(&si, 0, sizeof(si));
2863 si.cbSize = sizeof(si);
2864 si.fMask = SIF_PAGE | SIF_RANGE;
2865 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2866 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2867 "Vertical scrollbar is visible, should be invisible.\n");
2868 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2869 "reported page/range is %d (%d..%d)\n",
2870 si.nPage, si.nMin, si.nMax);
2872 /* Again, EM_SCROLL, with SB_LINEUP */
2873 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2874 memset(&si, 0, sizeof(si));
2875 si.cbSize = sizeof(si);
2876 si.fMask = SIF_PAGE | SIF_RANGE;
2877 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2878 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2879 "Vertical scrollbar is invisible, should be visible.\n");
2880 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2881 "reported page/range is %d (%d..%d)\n",
2882 si.nPage, si.nMin, si.nMax);
2884 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2885 memset(&si, 0, sizeof(si));
2886 si.cbSize = sizeof(si);
2887 si.fMask = SIF_PAGE | SIF_RANGE;
2888 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2889 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2890 "Vertical scrollbar is visible, should be invisible.\n");
2891 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2892 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2893 si.nPage, si.nMin, si.nMax);
2895 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2896 memset(&si, 0, sizeof(si));
2897 si.cbSize = sizeof(si);
2898 si.fMask = SIF_PAGE | SIF_RANGE;
2899 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2900 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2901 "Vertical scrollbar is invisible, should be visible.\n");
2902 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2903 "reported page/range is %d (%d..%d)\n",
2904 si.nPage, si.nMin, si.nMax);
2906 DestroyWindow(hwndRichEdit);
2909 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2910 hwndRichEdit = new_richedit(NULL);
2912 #define ENABLE_WS_VSCROLL(hwnd) \
2913 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2914 #define DISABLE_WS_VSCROLL(hwnd) \
2915 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2917 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2918 ENABLE_WS_VSCROLL(hwndRichEdit);
2919 memset(&si, 0, sizeof(si));
2920 si.cbSize = sizeof(si);
2921 si.fMask = SIF_PAGE | SIF_RANGE;
2922 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2923 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2924 "Vertical scrollbar is invisible, should be visible.\n");
2925 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2926 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2927 si.nPage, si.nMin, si.nMax);
2929 /* Ditto, see above */
2930 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2931 memset(&si, 0, sizeof(si));
2932 si.cbSize = sizeof(si);
2933 si.fMask = SIF_PAGE | SIF_RANGE;
2934 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2935 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2936 "Vertical scrollbar is invisible, should be visible.\n");
2937 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2938 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2939 si.nPage, si.nMin, si.nMax);
2941 /* Ditto, see above */
2942 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2943 memset(&si, 0, sizeof(si));
2944 si.cbSize = sizeof(si);
2945 si.fMask = SIF_PAGE | SIF_RANGE;
2946 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2947 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2948 "Vertical scrollbar is invisible, should be visible.\n");
2949 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2950 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2951 si.nPage, si.nMin, si.nMax);
2953 /* Ditto, see above */
2954 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2955 memset(&si, 0, sizeof(si));
2956 si.cbSize = sizeof(si);
2957 si.fMask = SIF_PAGE | SIF_RANGE;
2958 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2959 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2960 "Vertical scrollbar is invisible, should be visible.\n");
2961 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2962 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2963 si.nPage, si.nMin, si.nMax);
2965 /* Ditto, see above */
2966 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2967 memset(&si, 0, sizeof(si));
2968 si.cbSize = sizeof(si);
2969 si.fMask = SIF_PAGE | SIF_RANGE;
2970 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2971 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2972 "Vertical scrollbar is invisible, should be visible.\n");
2973 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2974 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2975 si.nPage, si.nMin, si.nMax);
2977 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2978 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2979 memset(&si, 0, sizeof(si));
2980 si.cbSize = sizeof(si);
2981 si.fMask = SIF_PAGE | SIF_RANGE;
2982 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2983 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2984 "Vertical scrollbar is visible, should be invisible.\n");
2985 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2986 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2987 si.nPage, si.nMin, si.nMax);
2989 DestroyWindow(hwndRichEdit);
2991 hwndRichEdit = new_richedit(NULL);
2993 DISABLE_WS_VSCROLL(hwndRichEdit);
2994 memset(&si, 0, sizeof(si));
2995 si.cbSize = sizeof(si);
2996 si.fMask = SIF_PAGE | SIF_RANGE;
2997 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2998 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2999 "Vertical scrollbar is visible, should be invisible.\n");
3000 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3001 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3002 si.nPage, si.nMin, si.nMax);
3004 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3005 memset(&si, 0, sizeof(si));
3006 si.cbSize = sizeof(si);
3007 si.fMask = SIF_PAGE | SIF_RANGE;
3008 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3009 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3010 "Vertical scrollbar is visible, should be invisible.\n");
3011 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3012 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3013 si.nPage, si.nMin, si.nMax);
3015 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3016 memset(&si, 0, sizeof(si));
3017 si.cbSize = sizeof(si);
3018 si.fMask = SIF_PAGE | SIF_RANGE;
3019 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3020 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3021 "Vertical scrollbar is visible, should be invisible.\n");
3022 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3023 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3024 si.nPage, si.nMin, si.nMax);
3026 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3027 memset(&si, 0, sizeof(si));
3028 si.cbSize = sizeof(si);
3029 si.fMask = SIF_PAGE | SIF_RANGE;
3030 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3031 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3032 "Vertical scrollbar is visible, should be invisible.\n");
3033 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3034 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3035 si.nPage, si.nMin, si.nMax);
3037 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3038 memset(&si, 0, sizeof(si));
3039 si.cbSize = sizeof(si);
3040 si.fMask = SIF_PAGE | SIF_RANGE;
3041 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3042 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3043 "Vertical scrollbar is invisible, should be visible.\n");
3044 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3045 "reported page/range is %d (%d..%d)\n",
3046 si.nPage, si.nMin, si.nMax);
3048 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3049 DISABLE_WS_VSCROLL(hwndRichEdit);
3050 memset(&si, 0, sizeof(si));
3051 si.cbSize = sizeof(si);
3052 si.fMask = SIF_PAGE | SIF_RANGE;
3053 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3054 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3055 "Vertical scrollbar is visible, should be invisible.\n");
3056 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3057 "reported page/range is %d (%d..%d)\n",
3058 si.nPage, si.nMin, si.nMax);
3060 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3061 memset(&si, 0, sizeof(si));
3062 si.cbSize = sizeof(si);
3063 si.fMask = SIF_PAGE | SIF_RANGE;
3064 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3065 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3066 "Vertical scrollbar is visible, should be invisible.\n");
3067 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3068 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3069 si.nPage, si.nMin, si.nMax);
3071 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3072 memset(&si, 0, sizeof(si));
3073 si.cbSize = sizeof(si);
3074 si.fMask = SIF_PAGE | SIF_RANGE;
3075 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3076 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3077 "Vertical scrollbar is invisible, should be visible.\n");
3078 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3079 "reported page/range is %d (%d..%d)\n",
3080 si.nPage, si.nMin, si.nMax);
3082 DISABLE_WS_VSCROLL(hwndRichEdit);
3083 memset(&si, 0, sizeof(si));
3084 si.cbSize = sizeof(si);
3085 si.fMask = SIF_PAGE | SIF_RANGE;
3086 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3087 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3088 "Vertical scrollbar is visible, should be invisible.\n");
3089 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3090 "reported page/range is %d (%d..%d)\n",
3091 si.nPage, si.nMin, si.nMax);
3093 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3094 EM_SCROLL will make visible any forcefully invisible scrollbar */
3095 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3096 memset(&si, 0, sizeof(si));
3097 si.cbSize = sizeof(si);
3098 si.fMask = SIF_PAGE | SIF_RANGE;
3099 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3100 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3101 "Vertical scrollbar is invisible, should be visible.\n");
3102 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3103 "reported page/range is %d (%d..%d)\n",
3104 si.nPage, si.nMin, si.nMax);
3106 DISABLE_WS_VSCROLL(hwndRichEdit);
3107 memset(&si, 0, sizeof(si));
3108 si.cbSize = sizeof(si);
3109 si.fMask = SIF_PAGE | SIF_RANGE;
3110 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3111 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3112 "Vertical scrollbar is visible, should be invisible.\n");
3113 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3114 "reported page/range is %d (%d..%d)\n",
3115 si.nPage, si.nMin, si.nMax);
3117 /* Again, EM_SCROLL, with SB_LINEUP */
3118 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3119 memset(&si, 0, sizeof(si));
3120 si.cbSize = sizeof(si);
3121 si.fMask = SIF_PAGE | SIF_RANGE;
3122 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3123 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3124 "Vertical scrollbar is invisible, should be visible.\n");
3125 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3126 "reported page/range is %d (%d..%d)\n",
3127 si.nPage, si.nMin, si.nMax);
3129 DestroyWindow(hwndRichEdit);
3131 /* This window proc models what is going on with Corman Lisp 3.0.
3132 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3133 force the scrollbar into visibility. Recursion should NOT happen
3134 as a result of this action.
3136 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3137 if (r) {
3138 richeditProc = cls.lpfnWndProc;
3139 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3140 cls.lpszClassName = "RicheditStupidOverride";
3141 if(!RegisterClassA(&cls)) assert(0);
3143 recursionLevel = 0;
3144 WM_SIZE_recursionLevel = 0;
3145 bailedOutOfRecursion = FALSE;
3146 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3147 ok(!bailedOutOfRecursion,
3148 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3150 recursionLevel = 0;
3151 WM_SIZE_recursionLevel = 0;
3152 bailedOutOfRecursion = FALSE;
3153 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3154 ok(!bailedOutOfRecursion,
3155 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3157 /* Unblock window in order to process WM_DESTROY */
3158 recursionLevel = 0;
3159 bailedOutOfRecursion = FALSE;
3160 WM_SIZE_recursionLevel = 0;
3161 DestroyWindow(hwndRichEdit);
3165 static void test_EM_SETUNDOLIMIT(void)
3167 /* cases we test for:
3168 * default behaviour - limiting at 100 undo's
3169 * undo disabled - setting a limit of 0
3170 * undo limited - undo limit set to some to some number, like 2
3171 * bad input - sending a negative number should default to 100 undo's */
3173 HWND hwndRichEdit = new_richedit(NULL);
3174 CHARRANGE cr;
3175 int i;
3176 int result;
3178 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3179 cr.cpMin = 0;
3180 cr.cpMax = 1;
3181 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3182 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3183 also, multiple pastes don't combine like WM_CHAR would */
3184 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3186 /* first case - check the default */
3187 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3188 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3189 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3190 for (i=0; i<100; i++) /* Undo 100 of them */
3191 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3192 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3193 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3195 /* second case - cannot undo */
3196 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3197 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3198 SendMessage(hwndRichEdit,
3199 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3200 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3201 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3203 /* third case - set it to an arbitrary number */
3204 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3205 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3206 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3207 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3208 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3209 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3210 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3211 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3212 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3213 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3214 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3215 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3216 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3217 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3219 /* fourth case - setting negative numbers should default to 100 undos */
3220 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3221 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3222 ok (result == 100,
3223 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3225 DestroyWindow(hwndRichEdit);
3228 static void test_ES_PASSWORD(void)
3230 /* This isn't hugely testable, so we're just going to run it through its paces */
3232 HWND hwndRichEdit = new_richedit(NULL);
3233 WCHAR result;
3235 /* First, check the default of a regular control */
3236 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3237 ok (result == 0,
3238 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3240 /* Now, set it to something normal */
3241 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3242 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3243 ok (result == 120,
3244 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3246 /* Now, set it to something odd */
3247 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3248 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3249 ok (result == 1234,
3250 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3251 DestroyWindow(hwndRichEdit);
3254 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3255 LPBYTE pbBuff,
3256 LONG cb,
3257 LONG *pcb)
3259 char** str = (char**)dwCookie;
3260 *pcb = cb;
3261 if (*pcb > 0) {
3262 memcpy(*str, pbBuff, *pcb);
3263 *str += *pcb;
3265 return 0;
3268 static void test_WM_SETTEXT(void)
3270 HWND hwndRichEdit = new_richedit(NULL);
3271 const char * TestItem1 = "TestSomeText";
3272 const char * TestItem2 = "TestSomeText\r";
3273 const char * TestItem2_after = "TestSomeText\r\n";
3274 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3275 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3276 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3277 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3278 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3279 const char * TestItem5_after = "TestSomeText TestSomeText";
3280 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3281 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3282 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3283 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3285 char buf[1024] = {0};
3286 LRESULT result;
3287 EDITSTREAM es;
3288 char * p;
3290 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3291 any solitary \r to be converted to \r\n on return. Properly paired
3292 \r\n are not affected. It also shows that the special sequence \r\r\n
3293 gets converted to a single space.
3296 #define TEST_SETTEXT(a, b) \
3297 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3298 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3299 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3300 ok (result == lstrlen(buf), \
3301 "WM_GETTEXT returned %ld instead of expected %u\n", \
3302 result, lstrlen(buf)); \
3303 result = strcmp(b, buf); \
3304 ok(result == 0, \
3305 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3307 TEST_SETTEXT(TestItem1, TestItem1)
3308 TEST_SETTEXT(TestItem2, TestItem2_after)
3309 TEST_SETTEXT(TestItem3, TestItem3_after)
3310 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3311 TEST_SETTEXT(TestItem4, TestItem4_after)
3312 TEST_SETTEXT(TestItem5, TestItem5_after)
3313 TEST_SETTEXT(TestItem6, TestItem6_after)
3314 TEST_SETTEXT(TestItem7, TestItem7_after)
3316 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
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_RTF), (LPARAM)&es);
3325 trace("EM_STREAMOUT produced:\n%s\n", buf);
3326 TEST_SETTEXT(buf, TestItem1)
3328 #undef TEST_SETTEXT
3329 DestroyWindow(hwndRichEdit);
3332 static void test_EM_STREAMOUT(void)
3334 HWND hwndRichEdit = new_richedit(NULL);
3335 int r;
3336 EDITSTREAM es;
3337 char buf[1024] = {0};
3338 char * p;
3340 const char * TestItem1 = "TestSomeText";
3341 const char * TestItem2 = "TestSomeText\r";
3342 const char * TestItem3 = "TestSomeText\r\n";
3344 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3345 p = buf;
3346 es.dwCookie = (DWORD_PTR)&p;
3347 es.dwError = 0;
3348 es.pfnCallback = test_WM_SETTEXT_esCallback;
3349 memset(buf, 0, sizeof(buf));
3350 SendMessage(hwndRichEdit, EM_STREAMOUT,
3351 (WPARAM)(SF_TEXT), (LPARAM)&es);
3352 r = strlen(buf);
3353 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3354 ok(strcmp(buf, TestItem1) == 0,
3355 "streamed text different, got %s\n", buf);
3357 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3358 p = buf;
3359 es.dwCookie = (DWORD_PTR)&p;
3360 es.dwError = 0;
3361 es.pfnCallback = test_WM_SETTEXT_esCallback;
3362 memset(buf, 0, sizeof(buf));
3363 SendMessage(hwndRichEdit, EM_STREAMOUT,
3364 (WPARAM)(SF_TEXT), (LPARAM)&es);
3365 r = strlen(buf);
3366 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3367 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3368 ok(strcmp(buf, TestItem3) == 0,
3369 "streamed text different from, got %s\n", buf);
3370 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3371 p = buf;
3372 es.dwCookie = (DWORD_PTR)&p;
3373 es.dwError = 0;
3374 es.pfnCallback = test_WM_SETTEXT_esCallback;
3375 memset(buf, 0, sizeof(buf));
3376 SendMessage(hwndRichEdit, EM_STREAMOUT,
3377 (WPARAM)(SF_TEXT), (LPARAM)&es);
3378 r = strlen(buf);
3379 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3380 ok(strcmp(buf, TestItem3) == 0,
3381 "streamed text different, got %s\n", buf);
3383 DestroyWindow(hwndRichEdit);
3386 static void test_EM_SETTEXTEX(void)
3388 HWND hwndRichEdit, parent;
3389 SCROLLINFO si;
3390 int sel_start, sel_end;
3391 SETTEXTEX setText;
3392 GETTEXTEX getText;
3393 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3394 'S', 'o', 'm', 'e',
3395 'T', 'e', 'x', 't', 0};
3396 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3397 't', 'S', 'o', 'm',
3398 'e', 'T', 'e', 'x',
3399 't', 't', 'S', 'o',
3400 'm', 'e', 'T', 'e',
3401 'x', 't', 0};
3402 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3403 '\r','t','S','o','m','e','T','e','x','t',0};
3404 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3405 'S', 'o', 'm', 'e',
3406 'T', 'e', 'x', 't',
3407 '\r', 0};
3408 const char * TestItem2_after = "TestSomeText\r\n";
3409 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3410 'S', 'o', 'm', 'e',
3411 'T', 'e', 'x', 't',
3412 '\r','\n','\r','\n', 0};
3413 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3414 'S', 'o', 'm', 'e',
3415 'T', 'e', 'x', 't',
3416 '\n','\n', 0};
3417 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3418 'S', 'o', 'm', 'e',
3419 'T', 'e', 'x', 't',
3420 '\r','\r', 0};
3421 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3422 'S', 'o', 'm', 'e',
3423 'T', 'e', 'x', 't',
3424 '\r','\r','\n','\r',
3425 '\n', 0};
3426 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3427 'S', 'o', 'm', 'e',
3428 'T', 'e', 'x', 't',
3429 ' ','\r', 0};
3430 #define MAX_BUF_LEN 1024
3431 WCHAR buf[MAX_BUF_LEN];
3432 char bufACP[MAX_BUF_LEN];
3433 char * p;
3434 int result;
3435 CHARRANGE cr;
3436 EDITSTREAM es;
3437 WNDCLASSA cls;
3439 /* Test the scroll position with and without a parent window.
3441 * For some reason the scroll position is 0 after EM_SETTEXTEX
3442 * with the ST_SELECTION flag only when the control has a parent
3443 * window, even though the selection is at the end. */
3444 cls.style = 0;
3445 cls.lpfnWndProc = DefWindowProcA;
3446 cls.cbClsExtra = 0;
3447 cls.cbWndExtra = 0;
3448 cls.hInstance = GetModuleHandleA(0);
3449 cls.hIcon = 0;
3450 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3451 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3452 cls.lpszMenuName = NULL;
3453 cls.lpszClassName = "ParentTestClass";
3454 if(!RegisterClassA(&cls)) assert(0);
3456 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3457 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3458 ok (parent != 0, "Failed to create parent window\n");
3460 hwndRichEdit = CreateWindowEx(0,
3461 RICHEDIT_CLASS, NULL,
3462 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3463 0, 0, 200, 60, parent, NULL,
3464 hmoduleRichEdit, NULL);
3466 setText.codepage = CP_ACP;
3467 setText.flags = ST_SELECTION;
3468 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3469 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3470 si.cbSize = sizeof(si);
3471 si.fMask = SIF_ALL;
3472 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3473 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3474 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3475 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3476 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3478 DestroyWindow(parent);
3480 /* Test without a parent window */
3481 hwndRichEdit = new_richedit(NULL);
3482 setText.codepage = CP_ACP;
3483 setText.flags = ST_SELECTION;
3484 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3485 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3486 si.cbSize = sizeof(si);
3487 si.fMask = SIF_ALL;
3488 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3489 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3490 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3491 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3492 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3494 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3495 * but this time it is because the selection is at the beginning. */
3496 setText.codepage = CP_ACP;
3497 setText.flags = ST_DEFAULT;
3498 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3499 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3500 si.cbSize = sizeof(si);
3501 si.fMask = SIF_ALL;
3502 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3503 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3504 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3505 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3506 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3508 setText.codepage = 1200; /* no constant for unicode */
3509 getText.codepage = 1200; /* no constant for unicode */
3510 getText.cb = MAX_BUF_LEN;
3511 getText.flags = GT_DEFAULT;
3512 getText.lpDefaultChar = NULL;
3513 getText.lpUsedDefChar = NULL;
3515 setText.flags = 0;
3516 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3517 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3518 ok(lstrcmpW(buf, TestItem1) == 0,
3519 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3521 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3522 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3524 setText.codepage = 1200; /* no constant for unicode */
3525 getText.codepage = 1200; /* no constant for unicode */
3526 getText.cb = MAX_BUF_LEN;
3527 getText.flags = GT_DEFAULT;
3528 getText.lpDefaultChar = NULL;
3529 getText.lpUsedDefChar = NULL;
3530 setText.flags = 0;
3531 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3532 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3533 ok(lstrcmpW(buf, TestItem2) == 0,
3534 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3536 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3537 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3538 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3539 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3541 /* Baseline test for just-enough buffer space for string */
3542 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3543 getText.codepage = 1200; /* no constant for unicode */
3544 getText.flags = GT_DEFAULT;
3545 getText.lpDefaultChar = NULL;
3546 getText.lpUsedDefChar = NULL;
3547 memset(buf, 0, MAX_BUF_LEN);
3548 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3549 ok(lstrcmpW(buf, TestItem2) == 0,
3550 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3552 /* When there is enough space for one character, but not both, of the CRLF
3553 pair at the end of the string, the CR is not copied at all. That is,
3554 the caller must not see CRLF pairs truncated to CR at the end of the
3555 string.
3557 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3558 getText.codepage = 1200; /* no constant for unicode */
3559 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3560 getText.lpDefaultChar = NULL;
3561 getText.lpUsedDefChar = NULL;
3562 memset(buf, 0, MAX_BUF_LEN);
3563 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3564 ok(lstrcmpW(buf, TestItem1) == 0,
3565 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3568 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3569 setText.codepage = 1200; /* no constant for unicode */
3570 getText.codepage = 1200; /* no constant for unicode */
3571 getText.cb = MAX_BUF_LEN;
3572 getText.flags = GT_DEFAULT;
3573 getText.lpDefaultChar = NULL;
3574 getText.lpUsedDefChar = NULL;
3575 setText.flags = 0;
3576 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3577 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3578 ok(lstrcmpW(buf, TestItem3_after) == 0,
3579 "EM_SETTEXTEX did not convert properly\n");
3581 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3582 setText.codepage = 1200; /* no constant for unicode */
3583 getText.codepage = 1200; /* no constant for unicode */
3584 getText.cb = MAX_BUF_LEN;
3585 getText.flags = GT_DEFAULT;
3586 getText.lpDefaultChar = NULL;
3587 getText.lpUsedDefChar = NULL;
3588 setText.flags = 0;
3589 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3590 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3591 ok(lstrcmpW(buf, TestItem3_after) == 0,
3592 "EM_SETTEXTEX did not convert properly\n");
3594 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3595 setText.codepage = 1200; /* no constant for unicode */
3596 getText.codepage = 1200; /* no constant for unicode */
3597 getText.cb = MAX_BUF_LEN;
3598 getText.flags = GT_DEFAULT;
3599 getText.lpDefaultChar = NULL;
3600 getText.lpUsedDefChar = NULL;
3601 setText.flags = 0;
3602 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3603 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3604 ok(lstrcmpW(buf, TestItem4_after) == 0,
3605 "EM_SETTEXTEX did not convert properly\n");
3607 /* !ST_SELECTION && Unicode && !\rtf */
3608 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3609 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3611 ok (result == 1,
3612 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3613 ok(lstrlenW(buf) == 0,
3614 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3616 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3617 setText.flags = 0;
3618 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3619 /* select some text */
3620 cr.cpMax = 1;
3621 cr.cpMin = 3;
3622 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3623 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3624 setText.flags = ST_SELECTION;
3625 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3626 ok(result == 0,
3627 "EM_SETTEXTEX with NULL lParam to replace selection"
3628 " with no text should return 0. Got %i\n",
3629 result);
3631 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3632 setText.flags = 0;
3633 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3634 /* select some text */
3635 cr.cpMax = 1;
3636 cr.cpMin = 3;
3637 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3638 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3639 setText.flags = ST_SELECTION;
3640 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3641 (WPARAM)&setText, (LPARAM) TestItem1);
3642 /* get text */
3643 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3644 ok(result == lstrlenW(TestItem1),
3645 "EM_SETTEXTEX with NULL lParam to replace selection"
3646 " with no text should return 0. Got %i\n",
3647 result);
3648 ok(lstrlenW(buf) == 22,
3649 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3650 lstrlenW(buf) );
3652 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3653 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3654 p = (char *)buf;
3655 es.dwCookie = (DWORD_PTR)&p;
3656 es.dwError = 0;
3657 es.pfnCallback = test_WM_SETTEXT_esCallback;
3658 memset(buf, 0, sizeof(buf));
3659 SendMessage(hwndRichEdit, EM_STREAMOUT,
3660 (WPARAM)(SF_RTF), (LPARAM)&es);
3661 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3663 /* !ST_SELECTION && !Unicode && \rtf */
3664 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3665 getText.codepage = 1200; /* no constant for unicode */
3666 getText.cb = MAX_BUF_LEN;
3667 getText.flags = GT_DEFAULT;
3668 getText.lpDefaultChar = NULL;
3669 getText.lpUsedDefChar = NULL;
3671 setText.flags = 0;
3672 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3673 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3674 ok(lstrcmpW(buf, TestItem1) == 0,
3675 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3677 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3678 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3679 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3680 getText.codepage = CP_ACP;
3681 getText.cb = MAX_BUF_LEN;
3682 getText.flags = GT_DEFAULT;
3683 getText.lpDefaultChar = NULL;
3684 getText.lpUsedDefChar = NULL;
3686 setText.flags = ST_SELECTION;
3687 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3688 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3689 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3690 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3691 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3693 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3694 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3695 p = (char *)buf;
3696 es.dwCookie = (DWORD_PTR)&p;
3697 es.dwError = 0;
3698 es.pfnCallback = test_WM_SETTEXT_esCallback;
3699 memset(buf, 0, sizeof(buf));
3700 SendMessage(hwndRichEdit, EM_STREAMOUT,
3701 (WPARAM)(SF_RTF), (LPARAM)&es);
3702 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3704 /* select some text */
3705 cr.cpMax = 1;
3706 cr.cpMin = 3;
3707 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3709 /* ST_SELECTION && !Unicode && \rtf */
3710 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3711 getText.codepage = 1200; /* no constant for unicode */
3712 getText.cb = MAX_BUF_LEN;
3713 getText.flags = GT_DEFAULT;
3714 getText.lpDefaultChar = NULL;
3715 getText.lpUsedDefChar = NULL;
3717 setText.flags = ST_SELECTION;
3718 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3719 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3720 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3722 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3723 setText.codepage = 1200; /* no constant for unicode */
3724 getText.codepage = CP_ACP;
3725 getText.cb = MAX_BUF_LEN;
3727 setText.flags = 0;
3728 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3729 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3731 /* select some text */
3732 cr.cpMax = 1;
3733 cr.cpMin = 3;
3734 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3736 /* ST_SELECTION && !Unicode && !\rtf */
3737 setText.codepage = CP_ACP;
3738 getText.codepage = 1200; /* no constant for unicode */
3739 getText.cb = MAX_BUF_LEN;
3740 getText.flags = GT_DEFAULT;
3741 getText.lpDefaultChar = NULL;
3742 getText.lpUsedDefChar = NULL;
3744 setText.flags = ST_SELECTION;
3745 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3746 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3747 ok(lstrcmpW(buf, TestItem1alt) == 0,
3748 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3749 " using ST_SELECTION and non-Unicode\n");
3751 /* Test setting text using rich text format */
3752 setText.flags = 0;
3753 setText.codepage = CP_ACP;
3754 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3755 getText.codepage = CP_ACP;
3756 getText.cb = MAX_BUF_LEN;
3757 getText.flags = GT_DEFAULT;
3758 getText.lpDefaultChar = NULL;
3759 getText.lpUsedDefChar = NULL;
3760 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3761 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3763 setText.flags = 0;
3764 setText.codepage = CP_ACP;
3765 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3766 getText.codepage = CP_ACP;
3767 getText.cb = MAX_BUF_LEN;
3768 getText.flags = GT_DEFAULT;
3769 getText.lpDefaultChar = NULL;
3770 getText.lpUsedDefChar = NULL;
3771 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3772 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3774 DestroyWindow(hwndRichEdit);
3777 static void test_EM_LIMITTEXT(void)
3779 int ret;
3781 HWND hwndRichEdit = new_richedit(NULL);
3783 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3784 * about setting the length to -1 for multiline edit controls doesn't happen.
3787 /* Don't check default gettextlimit case. That's done in other tests */
3789 /* Set textlimit to 100 */
3790 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3791 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3792 ok (ret == 100,
3793 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3795 /* Set textlimit to 0 */
3796 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3797 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3798 ok (ret == 65536,
3799 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3801 /* Set textlimit to -1 */
3802 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3803 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3804 ok (ret == -1,
3805 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3807 /* Set textlimit to -2 */
3808 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3809 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3810 ok (ret == -2,
3811 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3813 DestroyWindow (hwndRichEdit);
3817 static void test_EM_EXLIMITTEXT(void)
3819 int i, selBegin, selEnd, len1, len2;
3820 int result;
3821 char text[1024 + 1];
3822 char buffer[1024 + 1];
3823 int textlimit = 0; /* multiple of 100 */
3824 HWND hwndRichEdit = new_richedit(NULL);
3826 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3827 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3829 textlimit = 256000;
3830 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3831 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3832 /* set higher */
3833 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3835 textlimit = 1000;
3836 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3837 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3838 /* set lower */
3839 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3841 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3842 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3843 /* default for WParam = 0 */
3844 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3846 textlimit = sizeof(text)-1;
3847 memset(text, 'W', textlimit);
3848 text[sizeof(text)-1] = 0;
3849 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3850 /* maxed out text */
3851 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3853 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3854 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3855 len1 = selEnd - selBegin;
3857 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3858 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3859 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3860 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3861 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3862 len2 = selEnd - selBegin;
3864 ok(len1 != len2,
3865 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3866 len1,len2,i);
3868 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3869 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3870 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3871 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3872 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3873 len1 = selEnd - selBegin;
3875 ok(len1 != len2,
3876 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3877 len1,len2,i);
3879 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3880 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3881 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3882 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3883 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3884 len2 = selEnd - selBegin;
3886 ok(len1 == len2,
3887 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3888 len1,len2,i);
3890 /* set text up to the limit, select all the text, then add a char */
3891 textlimit = 5;
3892 memset(text, 'W', textlimit);
3893 text[textlimit] = 0;
3894 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3895 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3896 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3897 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3898 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3899 result = strcmp(buffer, "A");
3900 ok(0 == result, "got string = \"%s\"\n", buffer);
3902 /* WM_SETTEXT not limited */
3903 textlimit = 10;
3904 memset(text, 'W', textlimit);
3905 text[textlimit] = 0;
3906 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3907 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3908 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3909 i = strlen(buffer);
3910 ok(10 == i, "expected 10 chars\n");
3911 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3912 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3914 /* try inserting more text at end */
3915 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3916 ok(0 == i, "WM_CHAR wasn't processed\n");
3917 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3918 i = strlen(buffer);
3919 ok(10 == i, "expected 10 chars, got %i\n", i);
3920 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3921 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3923 /* try inserting text at beginning */
3924 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3925 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3926 ok(0 == i, "WM_CHAR wasn't processed\n");
3927 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3928 i = strlen(buffer);
3929 ok(10 == i, "expected 10 chars, got %i\n", i);
3930 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3931 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3933 /* WM_CHAR is limited */
3934 textlimit = 1;
3935 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3936 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3937 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3938 ok(0 == i, "WM_CHAR wasn't processed\n");
3939 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3940 ok(0 == i, "WM_CHAR wasn't processed\n");
3941 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3942 i = strlen(buffer);
3943 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3945 DestroyWindow(hwndRichEdit);
3948 static void test_EM_GETLIMITTEXT(void)
3950 int i;
3951 HWND hwndRichEdit = new_richedit(NULL);
3953 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3954 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3956 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3957 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3958 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3960 DestroyWindow(hwndRichEdit);
3963 static void test_WM_SETFONT(void)
3965 /* There is no invalid input or error conditions for this function.
3966 * NULL wParam and lParam just fall back to their default values
3967 * It should be noted that even if you use a gibberish name for your fonts
3968 * here, it will still work because the name is stored. They will display as
3969 * System, but will report their name to be whatever they were created as */
3971 HWND hwndRichEdit = new_richedit(NULL);
3972 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3973 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3974 FF_DONTCARE, "Marlett");
3975 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3976 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3977 FF_DONTCARE, "MS Sans Serif");
3978 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3979 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3980 FF_DONTCARE, "Courier");
3981 LOGFONTA sentLogFont;
3982 CHARFORMAT2A returnedCF2A;
3984 returnedCF2A.cbSize = sizeof(returnedCF2A);
3986 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3987 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
3988 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3990 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3991 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3992 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3993 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3995 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
3996 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3997 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3998 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3999 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4000 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4002 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4003 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4004 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4005 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4006 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4007 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4009 /* This last test is special since we send in NULL. We clear the variables
4010 * and just compare to "System" instead of the sent in font name. */
4011 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4012 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4013 returnedCF2A.cbSize = sizeof(returnedCF2A);
4015 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4016 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4017 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4018 ok (!strcmp("System",returnedCF2A.szFaceName),
4019 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4021 DestroyWindow(hwndRichEdit);
4025 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4026 LPBYTE pbBuff,
4027 LONG cb,
4028 LONG *pcb)
4030 const char** str = (const char**)dwCookie;
4031 int size = strlen(*str);
4032 if(size > 3) /* let's make it piecemeal for fun */
4033 size = 3;
4034 *pcb = cb;
4035 if (*pcb > size) {
4036 *pcb = size;
4038 if (*pcb > 0) {
4039 memcpy(pbBuff, *str, *pcb);
4040 *str += *pcb;
4042 return 0;
4045 static void test_EM_GETMODIFY(void)
4047 HWND hwndRichEdit = new_richedit(NULL);
4048 LRESULT result;
4049 SETTEXTEX setText;
4050 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4051 'S', 'o', 'm', 'e',
4052 'T', 'e', 'x', 't', 0};
4053 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4054 'S', 'o', 'm', 'e',
4055 'O', 't', 'h', 'e', 'r',
4056 'T', 'e', 'x', 't', 0};
4057 const char* streamText = "hello world";
4058 CHARFORMAT2 cf2;
4059 PARAFORMAT2 pf2;
4060 EDITSTREAM es;
4062 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4063 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4064 FF_DONTCARE, "Courier");
4066 setText.codepage = 1200; /* no constant for unicode */
4067 setText.flags = ST_KEEPUNDO;
4070 /* modify flag shouldn't be set when richedit is first created */
4071 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4072 ok (result == 0,
4073 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4075 /* setting modify flag should actually set it */
4076 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4077 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4078 ok (result != 0,
4079 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4081 /* clearing modify flag should actually clear it */
4082 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4083 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4084 ok (result == 0,
4085 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4087 /* setting font doesn't change modify flag */
4088 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4089 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4090 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4091 ok (result == 0,
4092 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4094 /* setting text should set modify flag */
4095 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4096 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4097 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4098 ok (result != 0,
4099 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4101 /* undo previous text doesn't reset modify flag */
4102 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4103 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4104 ok (result != 0,
4105 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4107 /* set text with no flag to keep undo stack should not set modify flag */
4108 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4109 setText.flags = 0;
4110 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4111 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4112 ok (result == 0,
4113 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4115 /* WM_SETTEXT doesn't modify */
4116 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4117 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4118 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4119 ok (result == 0,
4120 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4122 /* clear the text */
4123 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4124 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4125 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4126 ok (result == 0,
4127 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4129 /* replace text */
4130 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4131 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4132 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4133 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4134 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4135 ok (result != 0,
4136 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4138 /* copy/paste text 1 */
4139 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4140 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4141 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4142 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4143 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4144 ok (result != 0,
4145 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4147 /* copy/paste text 2 */
4148 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4149 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4150 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4151 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4152 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4153 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4154 ok (result != 0,
4155 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4157 /* press char */
4158 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4159 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4160 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4161 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4162 ok (result != 0,
4163 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4165 /* press del */
4166 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4167 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4168 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4169 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4170 ok (result != 0,
4171 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4173 /* set char format */
4174 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4175 cf2.cbSize = sizeof(CHARFORMAT2);
4176 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4177 (LPARAM) &cf2);
4178 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4179 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4180 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4181 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4182 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4183 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4184 ok (result != 0,
4185 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4187 /* set para format */
4188 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4189 pf2.cbSize = sizeof(PARAFORMAT2);
4190 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4191 (LPARAM) &pf2);
4192 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4193 pf2.wAlignment = PFA_RIGHT;
4194 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4195 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4196 ok (result == 0,
4197 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4199 /* EM_STREAM */
4200 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4201 es.dwCookie = (DWORD_PTR)&streamText;
4202 es.dwError = 0;
4203 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4204 SendMessage(hwndRichEdit, EM_STREAMIN,
4205 (WPARAM)(SF_TEXT), (LPARAM)&es);
4206 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4207 ok (result != 0,
4208 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4210 DestroyWindow(hwndRichEdit);
4213 struct exsetsel_s {
4214 long min;
4215 long max;
4216 long expected_retval;
4217 int expected_getsel_start;
4218 int expected_getsel_end;
4219 int _getsel_todo_wine;
4222 const struct exsetsel_s exsetsel_tests[] = {
4223 /* sanity tests */
4224 {5, 10, 10, 5, 10, 0},
4225 {15, 17, 17, 15, 17, 0},
4226 /* test cpMax > strlen() */
4227 {0, 100, 18, 0, 18, 1},
4228 /* test cpMin == cpMax */
4229 {5, 5, 5, 5, 5, 0},
4230 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4231 {-1, 0, 5, 5, 5, 0},
4232 {-1, 17, 5, 5, 5, 0},
4233 {-1, 18, 5, 5, 5, 0},
4234 /* test cpMin < 0 && cpMax < 0 */
4235 {-1, -1, 17, 17, 17, 0},
4236 {-4, -5, 17, 17, 17, 0},
4237 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4238 {0, -1, 18, 0, 18, 1},
4239 {17, -5, 18, 17, 18, 1},
4240 {18, -3, 17, 17, 17, 0},
4241 /* test if cpMin > cpMax */
4242 {15, 19, 18, 15, 18, 1},
4243 {19, 15, 18, 15, 18, 1}
4246 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4247 CHARRANGE cr;
4248 long result;
4249 int start, end;
4251 cr.cpMin = setsel->min;
4252 cr.cpMax = setsel->max;
4253 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4255 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4257 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4259 if (setsel->_getsel_todo_wine) {
4260 todo_wine {
4261 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);
4263 } else {
4264 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);
4268 static void test_EM_EXSETSEL(void)
4270 HWND hwndRichEdit = new_richedit(NULL);
4271 int i;
4272 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4274 /* sending some text to the window */
4275 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4276 /* 01234567890123456*/
4277 /* 10 */
4279 for (i = 0; i < num_tests; i++) {
4280 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4283 DestroyWindow(hwndRichEdit);
4286 static void test_EM_REPLACESEL(int redraw)
4288 HWND hwndRichEdit = new_richedit(NULL);
4289 char buffer[1024] = {0};
4290 int r;
4291 GETTEXTEX getText;
4292 CHARRANGE cr;
4294 /* sending some text to the window */
4295 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4296 /* 01234567890123456*/
4297 /* 10 */
4299 /* FIXME add more tests */
4300 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4301 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4302 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4303 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4304 r = strcmp(buffer, "testing");
4305 ok(0 == r, "expected %d, got %d\n", 0, r);
4307 DestroyWindow(hwndRichEdit);
4309 hwndRichEdit = new_richedit(NULL);
4311 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4312 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4314 /* Test behavior with carriage returns and newlines */
4315 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4316 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4317 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4318 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4319 r = strcmp(buffer, "RichEdit1");
4320 ok(0 == r, "expected %d, got %d\n", 0, r);
4321 getText.cb = 1024;
4322 getText.codepage = CP_ACP;
4323 getText.flags = GT_DEFAULT;
4324 getText.lpDefaultChar = NULL;
4325 getText.lpUsedDefChar = NULL;
4326 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4327 ok(strcmp(buffer, "RichEdit1") == 0,
4328 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4330 /* Test number of lines reported after EM_REPLACESEL */
4331 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4332 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4334 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4335 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4336 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4337 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4338 r = strcmp(buffer, "RichEdit1\r\n");
4339 ok(0 == r, "expected %d, got %d\n", 0, r);
4340 getText.cb = 1024;
4341 getText.codepage = CP_ACP;
4342 getText.flags = GT_DEFAULT;
4343 getText.lpDefaultChar = NULL;
4344 getText.lpUsedDefChar = NULL;
4345 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4346 ok(strcmp(buffer, "RichEdit1\r") == 0,
4347 "EM_GETTEXTEX returned incorrect string\n");
4349 /* Test number of lines reported after EM_REPLACESEL */
4350 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4351 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4353 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4354 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4355 returns the number of characters *inserted* into the control (after
4356 required conversions), but WinXP's riched20 returns the number of
4357 characters interpreted from the original lParam. Wine's builtin riched20
4358 implements the WinXP behavior.
4360 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4361 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4362 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4363 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4365 /* Test number of lines reported after EM_REPLACESEL */
4366 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4367 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4369 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4370 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4371 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4372 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4374 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4375 r = strcmp(buffer, "RichEdit1\r\n");
4376 ok(0 == r, "expected %d, got %d\n", 0, r);
4377 getText.cb = 1024;
4378 getText.codepage = CP_ACP;
4379 getText.flags = GT_DEFAULT;
4380 getText.lpDefaultChar = NULL;
4381 getText.lpUsedDefChar = NULL;
4382 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4383 ok(strcmp(buffer, "RichEdit1\r") == 0,
4384 "EM_GETTEXTEX returned incorrect string\n");
4386 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4387 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4388 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4389 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4391 /* The following tests show that richedit should handle the special \r\r\n
4392 sequence by turning it into a single space on insertion. However,
4393 EM_REPLACESEL on WinXP returns the number of characters in the original
4394 string.
4397 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4398 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4399 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4400 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4401 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4402 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4403 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4405 /* Test the actual string */
4406 getText.cb = 1024;
4407 getText.codepage = CP_ACP;
4408 getText.flags = GT_DEFAULT;
4409 getText.lpDefaultChar = NULL;
4410 getText.lpUsedDefChar = NULL;
4411 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4412 ok(strcmp(buffer, "\r\r") == 0,
4413 "EM_GETTEXTEX returned incorrect string\n");
4415 /* Test number of lines reported after EM_REPLACESEL */
4416 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4417 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4419 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4420 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4421 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4422 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4423 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4424 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4425 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4426 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4428 /* Test the actual string */
4429 getText.cb = 1024;
4430 getText.codepage = CP_ACP;
4431 getText.flags = GT_DEFAULT;
4432 getText.lpDefaultChar = NULL;
4433 getText.lpUsedDefChar = NULL;
4434 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4435 ok(strcmp(buffer, " ") == 0,
4436 "EM_GETTEXTEX returned incorrect string\n");
4438 /* Test number of lines reported after EM_REPLACESEL */
4439 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4440 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4442 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4443 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4444 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4445 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4446 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4447 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4448 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4449 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4451 /* Test the actual string */
4452 getText.cb = 1024;
4453 getText.codepage = CP_ACP;
4454 getText.flags = GT_DEFAULT;
4455 getText.lpDefaultChar = NULL;
4456 getText.lpUsedDefChar = NULL;
4457 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4458 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4459 "EM_GETTEXTEX returned incorrect string\n");
4461 /* Test number of lines reported after EM_REPLACESEL */
4462 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4463 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4465 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4466 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4467 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4468 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4469 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4470 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4471 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4472 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4474 /* Test the actual string */
4475 getText.cb = 1024;
4476 getText.codepage = CP_ACP;
4477 getText.flags = GT_DEFAULT;
4478 getText.lpDefaultChar = NULL;
4479 getText.lpUsedDefChar = NULL;
4480 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4481 ok(strcmp(buffer, " \r") == 0,
4482 "EM_GETTEXTEX returned incorrect string\n");
4484 /* Test number of lines reported after EM_REPLACESEL */
4485 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4486 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4488 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4489 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4490 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4491 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4492 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4493 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4494 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4495 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4497 /* Test the actual string */
4498 getText.cb = 1024;
4499 getText.codepage = CP_ACP;
4500 getText.flags = GT_DEFAULT;
4501 getText.lpDefaultChar = NULL;
4502 getText.lpUsedDefChar = NULL;
4503 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4504 ok(strcmp(buffer, " \r\r") == 0,
4505 "EM_GETTEXTEX returned incorrect string\n");
4507 /* Test number of lines reported after EM_REPLACESEL */
4508 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4509 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4511 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4512 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4513 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4514 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4515 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4516 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4517 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4518 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4520 /* Test the actual string */
4521 getText.cb = 1024;
4522 getText.codepage = CP_ACP;
4523 getText.flags = GT_DEFAULT;
4524 getText.lpDefaultChar = NULL;
4525 getText.lpUsedDefChar = NULL;
4526 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4527 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4528 "EM_GETTEXTEX returned incorrect string\n");
4530 /* Test number of lines reported after EM_REPLACESEL */
4531 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4532 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4534 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4535 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4536 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4537 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4538 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4539 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4540 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4542 /* Test the actual string */
4543 getText.cb = 1024;
4544 getText.codepage = CP_ACP;
4545 getText.flags = GT_DEFAULT;
4546 getText.lpDefaultChar = NULL;
4547 getText.lpUsedDefChar = NULL;
4548 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4549 ok(strcmp(buffer, "\r\r") == 0,
4550 "EM_GETTEXTEX returned incorrect string\n");
4552 /* Test number of lines reported after EM_REPLACESEL */
4553 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4554 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4556 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4557 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4558 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4559 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4560 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4561 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4562 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4563 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4565 /* Test the actual string */
4566 getText.cb = 1024;
4567 getText.codepage = CP_ACP;
4568 getText.flags = GT_DEFAULT;
4569 getText.lpDefaultChar = NULL;
4570 getText.lpUsedDefChar = NULL;
4571 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4572 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4573 "EM_GETTEXTEX returned incorrect string\n");
4575 /* Test number of lines reported after EM_REPLACESEL */
4576 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4577 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4579 if (!redraw)
4580 /* This is needed to avoid interferring with keybd_event calls
4581 * on other tests that simulate keyboard events. */
4582 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4584 DestroyWindow(hwndRichEdit);
4587 static void test_WM_PASTE(void)
4589 int result;
4590 char buffer[1024] = {0};
4591 const char* text1 = "testing paste\r";
4592 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4593 const char* text1_after = "testing paste\r\n";
4594 const char* text2 = "testing paste\r\rtesting paste";
4595 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4596 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4597 HWND hwndRichEdit = new_richedit(NULL);
4599 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4600 * to test the state of the modifiers (Ctrl/Alt/Shift).
4602 * Therefore Ctrl-<key> keystrokes need to be simulated with
4603 * keybd_event or by using SetKeyboardState to set the modifiers
4604 * and SendMessage to simulate the keystrokes.
4607 /* Sent keystrokes with keybd_event */
4608 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4609 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4610 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4611 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4612 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4614 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4615 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4617 SEND_CTRL_C(hwndRichEdit); /* Copy */
4618 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4619 SEND_CTRL_V(hwndRichEdit); /* Paste */
4620 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4621 /* Pasted text should be visible at this step */
4622 result = strcmp(text1_step1, buffer);
4623 ok(result == 0,
4624 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4626 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4627 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4628 /* Text should be the same as before (except for \r -> \r\n conversion) */
4629 result = strcmp(text1_after, buffer);
4630 ok(result == 0,
4631 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4633 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4634 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4635 SEND_CTRL_C(hwndRichEdit); /* Copy */
4636 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4637 SEND_CTRL_V(hwndRichEdit); /* Paste */
4638 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4639 /* Pasted text should be visible at this step */
4640 result = strcmp(text3, buffer);
4641 ok(result == 0,
4642 "test paste: strcmp = %i\n", result);
4643 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4644 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4645 /* Text should be the same as before (except for \r -> \r\n conversion) */
4646 result = strcmp(text2_after, buffer);
4647 ok(result == 0,
4648 "test paste: strcmp = %i\n", result);
4649 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4650 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4651 /* Text should revert to post-paste state */
4652 result = strcmp(buffer,text3);
4653 ok(result == 0,
4654 "test paste: strcmp = %i\n", result);
4656 #undef SEND_CTRL_C
4657 #undef SEND_CTRL_X
4658 #undef SEND_CTRL_V
4659 #undef SEND_CTRL_Z
4660 #undef SEND_CTRL_Y
4662 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4663 /* Send WM_CHAR to simulates Ctrl-V */
4664 SendMessage(hwndRichEdit, WM_CHAR, 22,
4665 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4666 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4667 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4668 result = strcmp(buffer,"");
4669 ok(result == 0,
4670 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4672 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4673 * with SetKeyboard state. */
4675 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4676 /* Simulates paste (Ctrl-V) */
4677 hold_key(VK_CONTROL);
4678 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4679 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4680 release_key(VK_CONTROL);
4681 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4682 result = strcmp(buffer,"paste");
4683 ok(result == 0,
4684 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4686 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4687 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4688 /* Simulates copy (Ctrl-C) */
4689 hold_key(VK_CONTROL);
4690 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4691 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4692 release_key(VK_CONTROL);
4693 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4694 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4695 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4696 result = strcmp(buffer,"testing");
4697 ok(result == 0,
4698 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4700 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4701 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4702 /* Simulates select all (Ctrl-A) */
4703 hold_key(VK_CONTROL);
4704 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4705 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4706 /* Simulates select cut (Ctrl-X) */
4707 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4708 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4709 release_key(VK_CONTROL);
4710 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4711 result = strcmp(buffer,"");
4712 ok(result == 0,
4713 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4714 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4715 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4716 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4717 result = strcmp(buffer,"cut\r\n");
4718 todo_wine ok(result == 0,
4719 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4720 /* Simulates undo (Ctrl-Z) */
4721 hold_key(VK_CONTROL);
4722 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4723 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4724 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4725 result = strcmp(buffer,"");
4726 ok(result == 0,
4727 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4728 /* Simulates redo (Ctrl-Y) */
4729 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4730 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4731 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4732 result = strcmp(buffer,"cut\r\n");
4733 todo_wine ok(result == 0,
4734 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4735 release_key(VK_CONTROL);
4737 DestroyWindow(hwndRichEdit);
4740 static void test_EM_FORMATRANGE(void)
4742 int i, tpp_x, tpp_y;
4743 HDC hdc;
4744 HWND hwndRichEdit = new_richedit(NULL);
4745 static const struct {
4746 const char *string; /* The string */
4747 int first; /* First 'pagebreak', 0 for don't care */
4748 int second; /* Second 'pagebreak', 0 for don't care */
4749 } fmtstrings[] = {
4750 {"WINE wine", 0, 0},
4751 {"WINE wineWine", 0, 0},
4752 {"WINE\r\nwine\r\nwine", 5, 10},
4753 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4754 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4757 hdc = GetDC(hwndRichEdit);
4758 ok(hdc != NULL, "Could not get HDC\n");
4760 /* Calculate the twips per pixel */
4761 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4762 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4764 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4766 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4768 FORMATRANGE fr;
4769 GETTEXTLENGTHEX gtl;
4770 SIZE stringsize;
4771 int r, len;
4773 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4775 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4776 gtl.codepage = CP_ACP;
4777 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4779 /* Get some size information for the string */
4780 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4782 /* Define the box to be half the width needed and a bit larger than the height.
4783 * Changes to the width means we have at least 2 pages. Changes to the height
4784 * is done so we can check the changing of fr.rc.bottom.
4786 fr.hdc = fr.hdcTarget = hdc;
4787 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4788 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4789 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4791 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4792 todo_wine {
4793 ok(r == len, "Expected %d, got %d\n", len, r);
4796 /* We know that the page can't hold the full string. See how many characters
4797 * are on the first one
4799 fr.chrg.cpMin = 0;
4800 fr.chrg.cpMax = -1;
4801 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4802 todo_wine {
4803 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4805 if (fmtstrings[i].first)
4806 todo_wine {
4807 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4809 else
4810 ok(r < len, "Expected < %d, got %d\n", len, r);
4812 /* Do another page */
4813 fr.chrg.cpMin = r;
4814 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4815 if (fmtstrings[i].second)
4816 todo_wine {
4817 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4819 else
4820 ok (r < len, "Expected < %d, got %d\n", len, r);
4822 /* There is at least on more page, but we don't care */
4824 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4825 todo_wine {
4826 ok(r == len, "Expected %d, got %d\n", len, r);
4830 ReleaseDC(NULL, hdc);
4831 DestroyWindow(hwndRichEdit);
4834 static int nCallbackCount = 0;
4836 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4837 LONG cb, LONG* pcb)
4839 const char text[] = {'t','e','s','t'};
4841 if (sizeof(text) <= cb)
4843 if ((int)dwCookie != nCallbackCount)
4845 *pcb = 0;
4846 return 0;
4849 memcpy (pbBuff, text, sizeof(text));
4850 *pcb = sizeof(text);
4852 nCallbackCount++;
4854 return 0;
4856 else
4857 return 1; /* indicates callback failed */
4860 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4861 LPBYTE pbBuff,
4862 LONG cb,
4863 LONG *pcb)
4865 const char** str = (const char**)dwCookie;
4866 int size = strlen(*str);
4867 *pcb = cb;
4868 if (*pcb > size) {
4869 *pcb = size;
4871 if (*pcb > 0) {
4872 memcpy(pbBuff, *str, *pcb);
4873 *str += *pcb;
4875 return 0;
4878 struct StringWithLength {
4879 int length;
4880 char *buffer;
4883 /* This callback is used to handled the null characters in a string. */
4884 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4885 LPBYTE pbBuff,
4886 LONG cb,
4887 LONG *pcb)
4889 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4890 int size = str->length;
4891 *pcb = cb;
4892 if (*pcb > size) {
4893 *pcb = size;
4895 if (*pcb > 0) {
4896 memcpy(pbBuff, str->buffer, *pcb);
4897 str->buffer += *pcb;
4898 str->length -= *pcb;
4900 return 0;
4903 static void test_EM_STREAMIN(void)
4905 HWND hwndRichEdit = new_richedit(NULL);
4906 LRESULT result;
4907 EDITSTREAM es;
4908 char buffer[1024] = {0};
4910 const char * streamText0 = "{\\rtf1 TestSomeText}";
4911 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4912 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4914 const char * streamText1 =
4915 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4916 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
4917 "}\r\n";
4919 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4920 const char * streamText2 =
4921 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
4922 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
4923 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
4924 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
4925 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
4926 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
4927 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4929 const char * streamText3 = "RichEdit1";
4931 struct StringWithLength cookieForStream4;
4932 const char * streamText4 =
4933 "This text just needs to be long enough to cause run to be split onto "
4934 "two separate lines and make sure the null terminating character is "
4935 "handled properly.\0";
4936 int length4 = strlen(streamText4) + 1;
4937 cookieForStream4.buffer = (char *)streamText4;
4938 cookieForStream4.length = length4;
4940 /* Minimal test without \par at the end */
4941 es.dwCookie = (DWORD_PTR)&streamText0;
4942 es.dwError = 0;
4943 es.pfnCallback = test_EM_STREAMIN_esCallback;
4944 SendMessage(hwndRichEdit, EM_STREAMIN,
4945 (WPARAM)(SF_RTF), (LPARAM)&es);
4947 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4948 ok (result == 12,
4949 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4950 result = strcmp (buffer,"TestSomeText");
4951 ok (result == 0,
4952 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4953 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4955 /* Native richedit 2.0 ignores last \par */
4956 es.dwCookie = (DWORD_PTR)&streamText0a;
4957 es.dwError = 0;
4958 es.pfnCallback = test_EM_STREAMIN_esCallback;
4959 SendMessage(hwndRichEdit, EM_STREAMIN,
4960 (WPARAM)(SF_RTF), (LPARAM)&es);
4962 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4963 ok (result == 12,
4964 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4965 result = strcmp (buffer,"TestSomeText");
4966 ok (result == 0,
4967 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4968 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4970 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4971 es.dwCookie = (DWORD_PTR)&streamText0b;
4972 es.dwError = 0;
4973 es.pfnCallback = test_EM_STREAMIN_esCallback;
4974 SendMessage(hwndRichEdit, EM_STREAMIN,
4975 (WPARAM)(SF_RTF), (LPARAM)&es);
4977 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4978 ok (result == 14,
4979 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4980 result = strcmp (buffer,"TestSomeText\r\n");
4981 ok (result == 0,
4982 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4983 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4985 es.dwCookie = (DWORD_PTR)&streamText1;
4986 es.dwError = 0;
4987 es.pfnCallback = test_EM_STREAMIN_esCallback;
4988 SendMessage(hwndRichEdit, EM_STREAMIN,
4989 (WPARAM)(SF_RTF), (LPARAM)&es);
4991 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4992 ok (result == 12,
4993 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4994 result = strcmp (buffer,"TestSomeText");
4995 ok (result == 0,
4996 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4997 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4999 es.dwCookie = (DWORD_PTR)&streamText2;
5000 es.dwError = 0;
5001 SendMessage(hwndRichEdit, EM_STREAMIN,
5002 (WPARAM)(SF_RTF), (LPARAM)&es);
5004 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5005 ok (result == 0,
5006 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5007 ok (strlen(buffer) == 0,
5008 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5009 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5011 es.dwCookie = (DWORD_PTR)&streamText3;
5012 es.dwError = 0;
5013 SendMessage(hwndRichEdit, EM_STREAMIN,
5014 (WPARAM)(SF_RTF), (LPARAM)&es);
5016 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5017 ok (result == 0,
5018 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5019 ok (strlen(buffer) == 0,
5020 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5021 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5023 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5024 es.dwError = 0;
5025 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5026 SendMessage(hwndRichEdit, EM_STREAMIN,
5027 (WPARAM)(SF_TEXT), (LPARAM)&es);
5029 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5030 ok (result == length4,
5031 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5032 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5034 DestroyWindow(hwndRichEdit);
5037 static void test_EM_StreamIn_Undo(void)
5039 /* The purpose of this test is to determine when a EM_StreamIn should be
5040 * undoable. This is important because WM_PASTE currently uses StreamIn and
5041 * pasting should always be undoable but streaming isn't always.
5043 * cases to test:
5044 * StreamIn plain text without SFF_SELECTION.
5045 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5046 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5047 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5048 * Feel free to add tests for other text modes or StreamIn things.
5052 HWND hwndRichEdit = new_richedit(NULL);
5053 LRESULT result;
5054 EDITSTREAM es;
5055 char buffer[1024] = {0};
5056 const char randomtext[] = "Some text";
5058 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5060 /* StreamIn, no SFF_SELECTION */
5061 es.dwCookie = nCallbackCount;
5062 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5063 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5064 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5065 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
5066 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5067 result = strcmp (buffer,"test");
5068 ok (result == 0,
5069 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5071 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5072 ok (result == FALSE,
5073 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5075 /* StreamIn, SFF_SELECTION, but nothing selected */
5076 es.dwCookie = nCallbackCount;
5077 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5078 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5079 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5080 SendMessage(hwndRichEdit, EM_STREAMIN,
5081 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5082 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5083 result = strcmp (buffer,"testSome text");
5084 ok (result == 0,
5085 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5087 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5088 ok (result == TRUE,
5089 "EM_STREAMIN with SFF_SELECTION but no selection set "
5090 "should create an undo\n");
5092 /* StreamIn, SFF_SELECTION, with a selection */
5093 es.dwCookie = nCallbackCount;
5094 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5095 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5096 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5097 SendMessage(hwndRichEdit, EM_STREAMIN,
5098 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5099 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5100 result = strcmp (buffer,"Sometesttext");
5101 ok (result == 0,
5102 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5104 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5105 ok (result == TRUE,
5106 "EM_STREAMIN with SFF_SELECTION and selection set "
5107 "should create an undo\n");
5109 DestroyWindow(hwndRichEdit);
5112 static BOOL is_em_settextex_supported(HWND hwnd)
5114 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5115 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5118 static void test_unicode_conversions(void)
5120 static const WCHAR tW[] = {'t',0};
5121 static const WCHAR teW[] = {'t','e',0};
5122 static const WCHAR textW[] = {'t','e','s','t',0};
5123 static const char textA[] = "test";
5124 char bufA[64];
5125 WCHAR bufW[64];
5126 HWND hwnd;
5127 int em_settextex_supported, ret;
5129 #define set_textA(hwnd, wm_set_text, txt) \
5130 do { \
5131 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5132 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5133 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5134 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5135 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5136 } while(0)
5137 #define expect_textA(hwnd, wm_get_text, txt) \
5138 do { \
5139 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5140 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5141 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5142 memset(bufA, 0xAA, sizeof(bufA)); \
5143 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5144 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5145 ret = lstrcmpA(bufA, txt); \
5146 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5147 } while(0)
5149 #define set_textW(hwnd, wm_set_text, txt) \
5150 do { \
5151 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5152 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5153 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5154 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5155 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5156 } while(0)
5157 #define expect_textW(hwnd, wm_get_text, txt) \
5158 do { \
5159 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5160 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5161 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5162 memset(bufW, 0xAA, sizeof(bufW)); \
5163 if (is_win9x) \
5165 assert(wm_get_text == EM_GETTEXTEX); \
5166 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5167 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5169 else \
5171 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5172 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5174 ret = lstrcmpW(bufW, txt); \
5175 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5176 } while(0)
5177 #define expect_empty(hwnd, wm_get_text) \
5178 do { \
5179 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5180 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5181 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5182 memset(bufA, 0xAA, sizeof(bufA)); \
5183 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5184 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5185 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5186 } while(0)
5188 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5189 0, 0, 200, 60, 0, 0, 0, 0);
5190 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5192 ret = IsWindowUnicode(hwnd);
5193 if (is_win9x)
5194 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
5195 else
5196 ok(ret, "RichEdit20W should be unicode under NT\n");
5198 /* EM_SETTEXTEX is supported starting from version 3.0 */
5199 em_settextex_supported = is_em_settextex_supported(hwnd);
5200 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5201 em_settextex_supported ? "" : "NOT ");
5203 expect_empty(hwnd, WM_GETTEXT);
5204 expect_empty(hwnd, EM_GETTEXTEX);
5206 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
5207 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5208 expect_textA(hwnd, WM_GETTEXT, "t");
5209 expect_textA(hwnd, EM_GETTEXTEX, "t");
5210 expect_textW(hwnd, EM_GETTEXTEX, tW);
5212 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
5213 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5214 expect_textA(hwnd, WM_GETTEXT, "te");
5215 expect_textA(hwnd, EM_GETTEXTEX, "te");
5216 expect_textW(hwnd, EM_GETTEXTEX, teW);
5218 set_textA(hwnd, WM_SETTEXT, NULL);
5219 expect_empty(hwnd, WM_GETTEXT);
5220 expect_empty(hwnd, EM_GETTEXTEX);
5222 if (is_win9x)
5223 set_textA(hwnd, WM_SETTEXT, textW);
5224 else
5225 set_textA(hwnd, WM_SETTEXT, textA);
5226 expect_textA(hwnd, WM_GETTEXT, textA);
5227 expect_textA(hwnd, EM_GETTEXTEX, textA);
5228 expect_textW(hwnd, EM_GETTEXTEX, textW);
5230 if (em_settextex_supported)
5232 set_textA(hwnd, EM_SETTEXTEX, textA);
5233 expect_textA(hwnd, WM_GETTEXT, textA);
5234 expect_textA(hwnd, EM_GETTEXTEX, textA);
5235 expect_textW(hwnd, EM_GETTEXTEX, textW);
5238 if (!is_win9x)
5240 set_textW(hwnd, WM_SETTEXT, textW);
5241 expect_textW(hwnd, WM_GETTEXT, textW);
5242 expect_textA(hwnd, WM_GETTEXT, textA);
5243 expect_textW(hwnd, EM_GETTEXTEX, textW);
5244 expect_textA(hwnd, EM_GETTEXTEX, textA);
5246 if (em_settextex_supported)
5248 set_textW(hwnd, EM_SETTEXTEX, textW);
5249 expect_textW(hwnd, WM_GETTEXT, textW);
5250 expect_textA(hwnd, WM_GETTEXT, textA);
5251 expect_textW(hwnd, EM_GETTEXTEX, textW);
5252 expect_textA(hwnd, EM_GETTEXTEX, textA);
5255 DestroyWindow(hwnd);
5257 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5258 0, 0, 200, 60, 0, 0, 0, 0);
5259 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5261 ret = IsWindowUnicode(hwnd);
5262 ok(!ret, "RichEdit20A should NOT be unicode\n");
5264 set_textA(hwnd, WM_SETTEXT, textA);
5265 expect_textA(hwnd, WM_GETTEXT, textA);
5266 expect_textA(hwnd, EM_GETTEXTEX, textA);
5267 expect_textW(hwnd, EM_GETTEXTEX, textW);
5269 if (em_settextex_supported)
5271 set_textA(hwnd, EM_SETTEXTEX, textA);
5272 expect_textA(hwnd, WM_GETTEXT, textA);
5273 expect_textA(hwnd, EM_GETTEXTEX, textA);
5274 expect_textW(hwnd, EM_GETTEXTEX, textW);
5277 if (!is_win9x)
5279 set_textW(hwnd, WM_SETTEXT, textW);
5280 expect_textW(hwnd, WM_GETTEXT, textW);
5281 expect_textA(hwnd, WM_GETTEXT, textA);
5282 expect_textW(hwnd, EM_GETTEXTEX, textW);
5283 expect_textA(hwnd, EM_GETTEXTEX, textA);
5285 if (em_settextex_supported)
5287 set_textW(hwnd, EM_SETTEXTEX, textW);
5288 expect_textW(hwnd, WM_GETTEXT, textW);
5289 expect_textA(hwnd, WM_GETTEXT, textA);
5290 expect_textW(hwnd, EM_GETTEXTEX, textW);
5291 expect_textA(hwnd, EM_GETTEXTEX, textA);
5294 DestroyWindow(hwnd);
5297 static void test_WM_CHAR(void)
5299 HWND hwnd;
5300 int ret;
5301 const char * char_list = "abc\rabc\r";
5302 const char * expected_content_single = "abcabc";
5303 const char * expected_content_multi = "abc\r\nabc\r\n";
5304 char buffer[64] = {0};
5305 const char * p;
5307 /* single-line control must IGNORE carriage returns */
5308 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5309 0, 0, 200, 60, 0, 0, 0, 0);
5310 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5312 p = char_list;
5313 while (*p != '\0') {
5314 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5315 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5316 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5317 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5318 p++;
5321 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5322 ret = strcmp(buffer, expected_content_single);
5323 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5325 DestroyWindow(hwnd);
5327 /* multi-line control inserts CR normally */
5328 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5329 0, 0, 200, 60, 0, 0, 0, 0);
5330 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5332 p = char_list;
5333 while (*p != '\0') {
5334 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5335 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5336 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5337 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5338 p++;
5341 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5342 ret = strcmp(buffer, expected_content_multi);
5343 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5345 DestroyWindow(hwnd);
5348 static void test_EM_GETTEXTLENGTHEX(void)
5350 HWND hwnd;
5351 GETTEXTLENGTHEX gtl;
5352 int ret;
5353 const char * base_string = "base string";
5354 const char * test_string = "a\nb\n\n\r\n";
5355 const char * test_string_after = "a";
5356 const char * test_string_2 = "a\rtest\rstring";
5357 char buffer[64] = {0};
5359 /* single line */
5360 if (!is_win9x)
5361 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5362 0, 0, 200, 60, 0, 0, 0, 0);
5363 else
5364 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5365 0, 0, 200, 60, 0, 0, 0, 0);
5366 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5368 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5369 gtl.codepage = CP_ACP;
5370 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5371 ok(ret == 0, "ret %d\n",ret);
5373 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5374 gtl.codepage = CP_ACP;
5375 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5376 ok(ret == 0, "ret %d\n",ret);
5378 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5380 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5381 gtl.codepage = CP_ACP;
5382 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5383 ok(ret == strlen(base_string), "ret %d\n",ret);
5385 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5386 gtl.codepage = CP_ACP;
5387 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5388 ok(ret == strlen(base_string), "ret %d\n",ret);
5390 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5392 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5393 gtl.codepage = CP_ACP;
5394 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5395 ok(ret == 1, "ret %d\n",ret);
5397 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5398 gtl.codepage = CP_ACP;
5399 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5400 ok(ret == 1, "ret %d\n",ret);
5402 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5403 ret = strcmp(buffer, test_string_after);
5404 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5406 DestroyWindow(hwnd);
5408 /* multi line */
5409 if (!is_win9x)
5410 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5411 0, 0, 200, 60, 0, 0, 0, 0);
5412 else
5413 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE,
5414 0, 0, 200, 60, 0, 0, 0, 0);
5415 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5417 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5418 gtl.codepage = CP_ACP;
5419 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5420 ok(ret == 0, "ret %d\n",ret);
5422 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5423 gtl.codepage = CP_ACP;
5424 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5425 ok(ret == 0, "ret %d\n",ret);
5427 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5429 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5430 gtl.codepage = CP_ACP;
5431 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5432 ok(ret == strlen(base_string), "ret %d\n",ret);
5434 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5435 gtl.codepage = CP_ACP;
5436 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5437 ok(ret == strlen(base_string), "ret %d\n",ret);
5439 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5441 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5442 gtl.codepage = CP_ACP;
5443 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5444 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5446 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5447 gtl.codepage = CP_ACP;
5448 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5449 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5451 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5453 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5454 gtl.codepage = CP_ACP;
5455 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5456 ok(ret == 10, "ret %d\n",ret);
5458 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5459 gtl.codepage = CP_ACP;
5460 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5461 ok(ret == 6, "ret %d\n",ret);
5463 DestroyWindow(hwnd);
5467 /* globals that parent and child access when checking event masks & notifications */
5468 static HWND eventMaskEditHwnd = 0;
5469 static int queriedEventMask;
5470 static int watchForEventMask = 0;
5472 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5473 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5475 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5477 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5479 return DefWindowProcA(hwnd, message, wParam, lParam);
5482 /* test event masks in combination with WM_COMMAND */
5483 static void test_eventMask(void)
5485 HWND parent;
5486 int ret, style;
5487 WNDCLASSA cls;
5488 const char text[] = "foo bar\n";
5489 int eventMask;
5491 /* register class to capture WM_COMMAND */
5492 cls.style = 0;
5493 cls.lpfnWndProc = ParentMsgCheckProcA;
5494 cls.cbClsExtra = 0;
5495 cls.cbWndExtra = 0;
5496 cls.hInstance = GetModuleHandleA(0);
5497 cls.hIcon = 0;
5498 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5499 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5500 cls.lpszMenuName = NULL;
5501 cls.lpszClassName = "EventMaskParentClass";
5502 if(!RegisterClassA(&cls)) assert(0);
5504 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5505 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5506 ok (parent != 0, "Failed to create parent window\n");
5508 eventMaskEditHwnd = new_richedit(parent);
5509 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5511 eventMask = ENM_CHANGE | ENM_UPDATE;
5512 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5513 ok(ret == ENM_NONE, "wrong event mask\n");
5514 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5515 ok(ret == eventMask, "failed to set event mask\n");
5517 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5518 queriedEventMask = 0; /* initialize to something other than we expect */
5519 watchForEventMask = EN_CHANGE;
5520 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5521 ok(ret == TRUE, "failed to set text\n");
5522 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5523 notification in response to WM_SETTEXT */
5524 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5525 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5527 /* check to see if EN_CHANGE is sent when redraw is turned off */
5528 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5529 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5530 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5531 /* redraw is disabled by making the window invisible. */
5532 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5533 queriedEventMask = 0; /* initialize to something other than we expect */
5534 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5535 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5536 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5537 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5538 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5540 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5541 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5542 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5543 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5544 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5545 watchForEventMask = EN_UPDATE;
5546 queriedEventMask = 0; /* initialize to something other than we expect */
5547 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5548 ok(queriedEventMask == 0,
5549 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5550 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5551 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5552 queriedEventMask = 0; /* initialize to something other than we expect */
5553 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5554 ok(queriedEventMask == eventMask,
5555 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5558 DestroyWindow(parent);
5561 static int received_WM_NOTIFY = 0;
5562 static int modify_at_WM_NOTIFY = 0;
5563 static HWND hwndRichedit_WM_NOTIFY;
5565 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5567 if(message == WM_NOTIFY)
5569 received_WM_NOTIFY = 1;
5570 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5572 return DefWindowProcA(hwnd, message, wParam, lParam);
5575 static void test_WM_NOTIFY(void)
5577 HWND parent;
5578 WNDCLASSA cls;
5579 CHARFORMAT2 cf2;
5581 /* register class to capture WM_NOTIFY */
5582 cls.style = 0;
5583 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5584 cls.cbClsExtra = 0;
5585 cls.cbWndExtra = 0;
5586 cls.hInstance = GetModuleHandleA(0);
5587 cls.hIcon = 0;
5588 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5589 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5590 cls.lpszMenuName = NULL;
5591 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5592 if(!RegisterClassA(&cls)) assert(0);
5594 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5595 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5596 ok (parent != 0, "Failed to create parent window\n");
5598 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5599 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5601 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5603 /* Notifications for selection change should only be sent when selection
5604 actually changes. EM_SETCHARFORMAT is one message that calls
5605 ME_CommitUndo, which should check whether message should be sent */
5606 received_WM_NOTIFY = 0;
5607 cf2.cbSize = sizeof(CHARFORMAT2);
5608 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5609 (LPARAM) &cf2);
5610 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5611 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5612 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5613 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5615 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5616 already at 0. */
5617 received_WM_NOTIFY = 0;
5618 modify_at_WM_NOTIFY = 0;
5619 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5620 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5621 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5623 received_WM_NOTIFY = 0;
5624 modify_at_WM_NOTIFY = 0;
5625 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5626 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5628 received_WM_NOTIFY = 0;
5629 modify_at_WM_NOTIFY = 0;
5630 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5631 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5632 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5634 /* Test for WM_NOTIFY messages with redraw disabled. */
5635 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5636 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5637 received_WM_NOTIFY = 0;
5638 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5639 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5640 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5642 DestroyWindow(hwndRichedit_WM_NOTIFY);
5643 DestroyWindow(parent);
5646 static void test_undo_coalescing(void)
5648 HWND hwnd;
5649 int result;
5650 char buffer[64] = {0};
5652 /* multi-line control inserts CR normally */
5653 if (!is_win9x)
5654 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5655 0, 0, 200, 60, 0, 0, 0, 0);
5656 else
5657 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE,
5658 0, 0, 200, 60, 0, 0, 0, 0);
5659 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5661 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5662 ok (result == FALSE, "Can undo after window creation.\n");
5663 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5664 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5665 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5666 ok (result == FALSE, "Can redo after window creation.\n");
5667 result = SendMessage(hwnd, EM_REDO, 0, 0);
5668 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5670 /* Test the effect of arrows keys during typing on undo transactions*/
5671 simulate_typing_characters(hwnd, "one two three");
5672 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5673 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5674 simulate_typing_characters(hwnd, " four five six");
5676 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5677 ok (result == FALSE, "Can redo before anything is undone.\n");
5678 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5679 ok (result == TRUE, "Cannot undo typed characters.\n");
5680 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5681 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5682 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5683 ok (result == TRUE, "Cannot redo after undo.\n");
5684 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5685 result = strcmp(buffer, "one two three");
5686 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5688 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5689 ok (result == TRUE, "Cannot undo typed characters.\n");
5690 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5691 ok (result == TRUE, "Failed to undo typed characters.\n");
5692 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5693 result = strcmp(buffer, "");
5694 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5696 /* Test the effect of focus changes during typing on undo transactions*/
5697 simulate_typing_characters(hwnd, "one two three");
5698 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5699 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5700 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5701 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5702 simulate_typing_characters(hwnd, " four five six");
5703 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5704 ok (result == TRUE, "Failed to undo typed characters.\n");
5705 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5706 result = strcmp(buffer, "one two three");
5707 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5709 /* Test the effect of the back key during typing on undo transactions */
5710 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5711 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5712 ok (result == TRUE, "Failed to clear the text.\n");
5713 simulate_typing_characters(hwnd, "one two threa");
5714 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5715 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5716 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5717 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5718 simulate_typing_characters(hwnd, "e four five six");
5719 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5720 ok (result == TRUE, "Failed to undo typed characters.\n");
5721 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5722 result = strcmp(buffer, "");
5723 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5725 /* Test the effect of the delete key during typing on undo transactions */
5726 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5727 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5728 ok(result == TRUE, "Failed to set the text.\n");
5729 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5730 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5731 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5732 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5733 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5734 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5735 ok (result == TRUE, "Failed to undo typed characters.\n");
5736 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5737 result = strcmp(buffer, "acd");
5738 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5739 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5740 ok (result == TRUE, "Failed to undo typed characters.\n");
5741 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5742 result = strcmp(buffer, "abcd");
5743 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5745 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5746 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5747 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5748 ok (result == TRUE, "Failed to clear the text.\n");
5749 simulate_typing_characters(hwnd, "one two three");
5750 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5751 ok (result == 0, "expected %d but got %d\n", 0, result);
5752 simulate_typing_characters(hwnd, " four five six");
5753 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5754 ok (result == TRUE, "Failed to undo typed characters.\n");
5755 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5756 result = strcmp(buffer, "one two three");
5757 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5758 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5759 ok (result == TRUE, "Failed to undo typed characters.\n");
5760 ok (result == TRUE, "Failed to undo typed characters.\n");
5761 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5762 result = strcmp(buffer, "");
5763 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5765 DestroyWindow(hwnd);
5768 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5770 int length;
5772 /* MSDN lied, length is actually the number of bytes. */
5773 length = bytes / sizeof(WCHAR);
5774 switch(code)
5776 case WB_ISDELIMITER:
5777 return text[pos] == 'X';
5778 case WB_LEFT:
5779 case WB_MOVEWORDLEFT:
5780 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5781 return pos-1;
5782 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5783 case WB_LEFTBREAK:
5784 pos--;
5785 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5786 pos--;
5787 return pos;
5788 case WB_RIGHT:
5789 case WB_MOVEWORDRIGHT:
5790 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5791 return pos+1;
5792 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5793 case WB_RIGHTBREAK:
5794 pos++;
5795 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5796 pos++;
5797 return pos;
5798 default:
5799 ok(FALSE, "Unexpected code %d\n", code);
5800 break;
5802 return 0;
5805 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5806 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5808 static void test_word_movement(void)
5810 HWND hwnd;
5811 int result;
5812 int sel_start, sel_end;
5813 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5815 /* multi-line control inserts CR normally */
5816 hwnd = new_richedit(NULL);
5818 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5819 ok (result == TRUE, "Failed to clear the text.\n");
5820 SendMessage(hwnd, EM_SETSEL, 0, 0);
5821 /* |one two three */
5823 SEND_CTRL_RIGHT(hwnd);
5824 /* one |two three */
5825 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5826 ok(sel_start == sel_end, "Selection should be empty\n");
5827 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5829 SEND_CTRL_RIGHT(hwnd);
5830 /* one two |three */
5831 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5832 ok(sel_start == sel_end, "Selection should be empty\n");
5833 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5835 SEND_CTRL_LEFT(hwnd);
5836 /* one |two three */
5837 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5838 ok(sel_start == sel_end, "Selection should be empty\n");
5839 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5841 SEND_CTRL_LEFT(hwnd);
5842 /* |one two three */
5843 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5844 ok(sel_start == sel_end, "Selection should be empty\n");
5845 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5847 SendMessage(hwnd, EM_SETSEL, 8, 8);
5848 /* one two | three */
5849 SEND_CTRL_RIGHT(hwnd);
5850 /* one two |three */
5851 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5852 ok(sel_start == sel_end, "Selection should be empty\n");
5853 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5855 SendMessage(hwnd, EM_SETSEL, 11, 11);
5856 /* one two th|ree */
5857 SEND_CTRL_LEFT(hwnd);
5858 /* one two |three */
5859 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5860 ok(sel_start == sel_end, "Selection should be empty\n");
5861 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5863 /* Test with a custom word break procedure that uses X as the delimiter. */
5864 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5865 ok (result == TRUE, "Failed to clear the text.\n");
5866 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5867 /* |one twoXthree */
5868 SEND_CTRL_RIGHT(hwnd);
5869 /* one twoX|three */
5870 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5871 ok(sel_start == sel_end, "Selection should be empty\n");
5872 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5874 DestroyWindow(hwnd);
5876 /* Make sure the behaviour is the same with a unicode richedit window,
5877 * and using unicode functions. */
5878 if (is_win9x)
5880 skip("Cannot test with unicode richedit window\n");
5881 return;
5884 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
5885 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5886 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5888 /* Test with a custom word break procedure that uses X as the delimiter. */
5889 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
5890 ok (result == TRUE, "Failed to clear the text.\n");
5891 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5892 /* |one twoXthree */
5893 SEND_CTRL_RIGHT(hwnd);
5894 /* one twoX|three */
5895 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5896 ok(sel_start == sel_end, "Selection should be empty\n");
5897 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5899 DestroyWindow(hwnd);
5902 static void test_EM_CHARFROMPOS(void)
5904 HWND hwnd;
5905 int result;
5906 RECT rcClient;
5907 POINTL point;
5908 point.x = 0;
5909 point.y = 40;
5911 /* multi-line control inserts CR normally */
5912 hwnd = new_richedit(NULL);
5913 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5914 (LPARAM)"one two three four five six seven\reight");
5916 GetClientRect(hwnd, &rcClient);
5918 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5919 ok(result == 34, "expected character index of 34 but got %d\n", result);
5921 /* Test with points outside the bounds of the richedit control. */
5922 point.x = -1;
5923 point.y = 40;
5924 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5925 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
5927 point.x = 1000;
5928 point.y = 0;
5929 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5930 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
5932 point.x = 1000;
5933 point.y = 40;
5934 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5935 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
5937 point.x = 1000;
5938 point.y = -1;
5939 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5940 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
5942 point.x = 1000;
5943 point.y = rcClient.bottom + 1;
5944 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5945 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
5947 point.x = 1000;
5948 point.y = rcClient.bottom;
5949 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5950 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
5952 DestroyWindow(hwnd);
5955 static void test_word_wrap(void)
5957 HWND hwnd;
5958 POINTL point = {0, 60}; /* This point must be below the first line */
5959 const char *text = "Must be long enough to test line wrapping";
5960 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
5961 int res, pos, lines;
5963 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
5964 * when specified on window creation and set later. */
5965 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5966 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5967 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5968 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5969 ok(res, "WM_SETTEXT failed.\n");
5970 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5971 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5972 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5973 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5975 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
5976 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5977 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5978 DestroyWindow(hwnd);
5980 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
5981 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5982 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5984 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5985 ok(res, "WM_SETTEXT failed.\n");
5986 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5987 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5988 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5989 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5991 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5992 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5993 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5994 DestroyWindow(hwnd);
5996 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
5997 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5998 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5999 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6000 ok(res, "WM_SETTEXT failed.\n");
6001 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6002 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6004 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6005 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6006 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6007 DestroyWindow(hwnd);
6009 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6010 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6011 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6012 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6013 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6014 ok(res, "WM_SETTEXT failed.\n");
6015 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6016 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6018 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6019 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6020 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6022 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6023 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6024 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6025 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6026 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6028 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6029 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6030 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6031 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6032 DestroyWindow(hwnd);
6034 /* Test to see if wrapping happens with redraw disabled. */
6035 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6036 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6037 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6038 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6039 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6040 ok(res, "EM_REPLACESEL failed.\n");
6041 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6042 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6043 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6044 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6045 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6047 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6048 DestroyWindow(hwnd);
6051 static void test_autoscroll(void)
6053 HWND hwnd = new_richedit(NULL);
6054 int lines, ret, redraw;
6055 POINT pt;
6057 for (redraw = 0; redraw <= 1; redraw++) {
6058 trace("testing with WM_SETREDRAW=%d\n", redraw);
6059 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6060 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6061 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6062 ok(lines == 8, "%d lines instead of 8\n", lines);
6063 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6064 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6065 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6066 ret = GetWindowLong(hwnd, GWL_STYLE);
6067 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6069 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6070 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6071 ok(lines == 1, "%d lines instead of 1\n", lines);
6072 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6073 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6074 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6075 ret = GetWindowLong(hwnd, GWL_STYLE);
6076 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6079 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6080 DestroyWindow(hwnd);
6082 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6083 * auto vertical/horizontal scrolling options. */
6084 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6085 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6086 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6087 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6088 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6089 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6090 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6091 ret = GetWindowLong(hwnd, GWL_STYLE);
6092 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6093 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6094 DestroyWindow(hwnd);
6096 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6097 WS_POPUP|ES_MULTILINE,
6098 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6099 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6100 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6101 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6102 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6103 ret = GetWindowLong(hwnd, GWL_STYLE);
6104 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6105 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6106 DestroyWindow(hwnd);
6110 static void test_format_rect(void)
6112 HWND hwnd;
6113 RECT rc, expected, clientRect;
6114 int n;
6115 DWORD options;
6117 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6118 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6119 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6120 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6122 GetClientRect(hwnd, &clientRect);
6124 expected = clientRect;
6125 expected.left += 1;
6126 expected.right -= 1;
6127 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6128 ok(rc.top == expected.top && rc.left == expected.left &&
6129 rc.bottom == expected.bottom && rc.right == expected.right,
6130 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6131 rc.top, rc.left, rc.bottom, rc.right,
6132 expected.top, expected.left, expected.bottom, expected.right);
6134 for (n = -3; n <= 3; n++)
6136 rc = clientRect;
6137 rc.top += n;
6138 rc.left += n;
6139 rc.bottom -= n;
6140 rc.right -= n;
6141 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6143 expected = rc;
6144 expected.top = max(0, rc.top);
6145 expected.left = max(0, rc.left);
6146 expected.bottom = min(clientRect.bottom, rc.bottom);
6147 expected.right = min(clientRect.right, rc.right);
6148 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6149 ok(rc.top == expected.top && rc.left == expected.left &&
6150 rc.bottom == expected.bottom && rc.right == expected.right,
6151 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6152 n, rc.top, rc.left, rc.bottom, rc.right,
6153 expected.top, expected.left, expected.bottom, expected.right);
6156 rc = clientRect;
6157 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6158 expected = clientRect;
6159 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6160 ok(rc.top == expected.top && rc.left == expected.left &&
6161 rc.bottom == expected.bottom && rc.right == expected.right,
6162 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6163 rc.top, rc.left, rc.bottom, rc.right,
6164 expected.top, expected.left, expected.bottom, expected.right);
6166 /* Adding the selectionbar adds the selectionbar width to the left side. */
6167 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6168 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6169 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6170 expected.left += 8; /* selection bar width */
6171 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6172 ok(rc.top == expected.top && rc.left == expected.left &&
6173 rc.bottom == expected.bottom && rc.right == expected.right,
6174 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6175 rc.top, rc.left, rc.bottom, rc.right,
6176 expected.top, expected.left, expected.bottom, expected.right);
6178 rc = clientRect;
6179 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6180 expected = clientRect;
6181 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6182 ok(rc.top == expected.top && rc.left == expected.left &&
6183 rc.bottom == expected.bottom && rc.right == expected.right,
6184 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6185 rc.top, rc.left, rc.bottom, rc.right,
6186 expected.top, expected.left, expected.bottom, expected.right);
6188 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6189 * even if the left side is already 0. */
6190 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6191 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6192 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6193 expected.left -= 8; /* selection bar width */
6194 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6195 ok(rc.top == expected.top && rc.left == expected.left &&
6196 rc.bottom == expected.bottom && rc.right == expected.right,
6197 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6198 rc.top, rc.left, rc.bottom, rc.right,
6199 expected.top, expected.left, expected.bottom, expected.right);
6201 /* Set the absolute value of the formatting rectangle. */
6202 rc = clientRect;
6203 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6204 expected = clientRect;
6205 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6206 ok(rc.top == expected.top && rc.left == expected.left &&
6207 rc.bottom == expected.bottom && rc.right == expected.right,
6208 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6209 n, rc.top, rc.left, rc.bottom, rc.right,
6210 expected.top, expected.left, expected.bottom, expected.right);
6212 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6213 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6214 * tests show that this isn't true. */
6215 rc.top = 15;
6216 rc.left = 15;
6217 rc.bottom = clientRect.bottom - 15;
6218 rc.right = clientRect.right - 15;
6219 expected = rc;
6220 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6221 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6222 ok(rc.top == expected.top && rc.left == expected.left &&
6223 rc.bottom == expected.bottom && rc.right == expected.right,
6224 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6225 rc.top, rc.left, rc.bottom, rc.right,
6226 expected.top, expected.left, expected.bottom, expected.right);
6228 /* For some reason it does not limit the values to the client rect with
6229 * a WPARAM value of 1. */
6230 rc.top = -15;
6231 rc.left = -15;
6232 rc.bottom = clientRect.bottom + 15;
6233 rc.right = clientRect.right + 15;
6234 expected = rc;
6235 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6236 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6237 ok(rc.top == expected.top && rc.left == expected.left &&
6238 rc.bottom == expected.bottom && rc.right == expected.right,
6239 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6240 rc.top, rc.left, rc.bottom, rc.right,
6241 expected.top, expected.left, expected.bottom, expected.right);
6243 DestroyWindow(hwnd);
6245 /* The extended window style affects the formatting rectangle. */
6246 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6247 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6248 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6249 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6251 GetClientRect(hwnd, &clientRect);
6253 expected = clientRect;
6254 expected.left += 1;
6255 expected.top += 1;
6256 expected.right -= 1;
6257 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6258 ok(rc.top == expected.top && rc.left == expected.left &&
6259 rc.bottom == expected.bottom && rc.right == expected.right,
6260 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6261 rc.top, rc.left, rc.bottom, rc.right,
6262 expected.top, expected.left, expected.bottom, expected.right);
6264 rc = clientRect;
6265 rc.top += 5;
6266 rc.left += 5;
6267 rc.bottom -= 5;
6268 rc.right -= 5;
6269 expected = rc;
6270 expected.top -= 1;
6271 expected.left -= 1;
6272 expected.right += 1;
6273 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6274 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6275 ok(rc.top == expected.top && rc.left == expected.left &&
6276 rc.bottom == expected.bottom && rc.right == expected.right,
6277 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6278 rc.top, rc.left, rc.bottom, rc.right,
6279 expected.top, expected.left, expected.bottom, expected.right);
6281 DestroyWindow(hwnd);
6284 static void test_WM_GETDLGCODE(void)
6286 HWND hwnd;
6287 UINT res, expected;
6288 MSG msg;
6290 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6292 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6293 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6294 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6295 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6296 msg.hwnd = hwnd;
6297 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6298 expected = expected | DLGC_WANTMESSAGE;
6299 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6300 res, expected);
6301 DestroyWindow(hwnd);
6303 msg.message = WM_KEYDOWN;
6304 msg.wParam = VK_RETURN;
6305 msg.lParam = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) | 0x0001;
6306 msg.pt.x = 0;
6307 msg.pt.y = 0;
6308 msg.time = GetTickCount();
6310 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6311 ES_MULTILINE|ES_WANTRETURN|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 = expected | DLGC_WANTMESSAGE;
6317 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6318 res, expected);
6319 DestroyWindow(hwnd);
6321 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6322 ES_MULTILINE|WS_POPUP,
6323 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6324 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6325 msg.hwnd = hwnd;
6326 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6327 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6328 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6329 res, expected);
6330 DestroyWindow(hwnd);
6332 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6333 ES_WANTRETURN|WS_POPUP,
6334 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6335 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6336 msg.hwnd = hwnd;
6337 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6338 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6339 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6340 res, expected);
6341 DestroyWindow(hwnd);
6343 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6344 WS_POPUP,
6345 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6346 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6347 msg.hwnd = hwnd;
6348 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6349 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6350 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6351 res, expected);
6352 DestroyWindow(hwnd);
6354 msg.wParam = VK_TAB;
6355 msg.lParam = MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) | 0x0001;
6357 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6358 ES_MULTILINE|WS_POPUP,
6359 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6360 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6361 msg.hwnd = hwnd;
6362 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6363 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6364 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6365 res, expected);
6366 DestroyWindow(hwnd);
6368 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6369 WS_POPUP,
6370 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6371 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6372 msg.hwnd = hwnd;
6373 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6374 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6375 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6376 res, expected);
6377 DestroyWindow(hwnd);
6379 hold_key(VK_CONTROL);
6381 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6382 ES_MULTILINE|WS_POPUP,
6383 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6384 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6385 msg.hwnd = hwnd;
6386 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6387 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6388 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6389 res, expected);
6390 DestroyWindow(hwnd);
6392 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6393 WS_POPUP,
6394 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6395 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6396 msg.hwnd = hwnd;
6397 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6398 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6399 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6400 res, expected);
6401 DestroyWindow(hwnd);
6403 release_key(VK_CONTROL);
6405 msg.wParam = 'a';
6406 msg.lParam = MapVirtualKey('a', MAPVK_VK_TO_VSC) | 0x0001;
6408 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6409 ES_MULTILINE|WS_POPUP,
6410 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6411 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6412 msg.hwnd = hwnd;
6413 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6414 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6415 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6416 res, expected);
6417 DestroyWindow(hwnd);
6419 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6420 WS_POPUP,
6421 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6422 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6423 msg.hwnd = hwnd;
6424 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6425 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6426 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6427 res, expected);
6428 DestroyWindow(hwnd);
6430 msg.message = WM_CHAR;
6432 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6433 ES_MULTILINE|WS_POPUP,
6434 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6435 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6436 msg.hwnd = hwnd;
6437 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6438 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6439 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6440 res, expected);
6441 DestroyWindow(hwnd);
6443 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6444 WS_POPUP,
6445 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6446 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6447 msg.hwnd = hwnd;
6448 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6449 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6450 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6451 res, expected);
6452 DestroyWindow(hwnd);
6455 static void test_zoom(void)
6457 HWND hwnd;
6458 UINT ret;
6459 RECT rc;
6460 POINT pt;
6461 int numerator, denominator;
6463 hwnd = new_richedit(NULL);
6464 GetClientRect(hwnd, &rc);
6465 pt.x = (rc.right - rc.left) / 2;
6466 pt.y = (rc.bottom - rc.top) / 2;
6467 ClientToScreen(hwnd, &pt);
6469 /* Test initial zoom value */
6470 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6471 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6472 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6473 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6475 /* test scroll wheel */
6476 hold_key(VK_CONTROL);
6477 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6478 MAKELPARAM(pt.x, pt.y));
6479 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6480 release_key(VK_CONTROL);
6482 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6483 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6484 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6485 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6487 /* Test how much the mouse wheel can zoom in and out. */
6488 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)490, (LPARAM)100);
6489 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6491 hold_key(VK_CONTROL);
6492 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6493 MAKELPARAM(pt.x, pt.y));
6494 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6495 release_key(VK_CONTROL);
6497 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6498 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6499 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6500 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6502 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)491, (LPARAM)100);
6503 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6505 hold_key(VK_CONTROL);
6506 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6507 MAKELPARAM(pt.x, pt.y));
6508 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6509 release_key(VK_CONTROL);
6511 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6512 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6513 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6514 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6516 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)20, (LPARAM)100);
6517 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6519 hold_key(VK_CONTROL);
6520 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6521 MAKELPARAM(pt.x, pt.y));
6522 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6523 release_key(VK_CONTROL);
6525 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6526 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6527 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6528 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6530 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)19, (LPARAM)100);
6531 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6533 hold_key(VK_CONTROL);
6534 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6535 MAKELPARAM(pt.x, pt.y));
6536 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6537 release_key(VK_CONTROL);
6539 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6540 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6541 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6542 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6544 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6545 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)50, (LPARAM)13);
6546 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6548 hold_key(VK_CONTROL);
6549 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6550 MAKELPARAM(pt.x, pt.y));
6551 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6552 release_key(VK_CONTROL);
6554 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6555 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6556 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6557 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6559 /* Test bounds checking on EM_SETZOOM */
6560 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)127);
6561 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6563 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)127, (LPARAM)2);
6564 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6566 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)128);
6567 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6569 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6570 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6571 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6572 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6574 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)128, (LPARAM)2);
6575 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6577 /* See if negative numbers are accepted. */
6578 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)-100, (LPARAM)-100);
6579 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6581 /* See if negative numbers are accepted. */
6582 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)100);
6583 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6585 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6586 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6587 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6588 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6590 /* Reset the zoom value */
6591 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)0);
6592 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6594 DestroyWindow(hwnd);
6597 START_TEST( editor )
6599 /* Must explicitly LoadLibrary(). The test has no references to functions in
6600 * RICHED20.DLL, so the linker doesn't actually link to it. */
6601 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
6602 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
6604 is_win9x = GetVersion() & 0x80000000;
6606 test_WM_CHAR();
6607 test_EM_FINDTEXT();
6608 test_EM_GETLINE();
6609 test_EM_POSFROMCHAR();
6610 test_EM_SCROLLCARET();
6611 test_EM_SCROLL();
6612 test_scrollbar_visibility();
6613 test_WM_SETTEXT();
6614 test_EM_LINELENGTH();
6615 test_EM_SETCHARFORMAT();
6616 test_EM_SETTEXTMODE();
6617 test_TM_PLAINTEXT();
6618 test_EM_SETOPTIONS();
6619 test_WM_GETTEXT();
6620 test_EM_GETTEXTRANGE();
6621 test_EM_GETSELTEXT();
6622 test_EM_SETUNDOLIMIT();
6623 test_ES_PASSWORD();
6624 test_EM_SETTEXTEX();
6625 test_EM_LIMITTEXT();
6626 test_EM_EXLIMITTEXT();
6627 test_EM_GETLIMITTEXT();
6628 test_WM_SETFONT();
6629 test_EM_GETMODIFY();
6630 test_EM_EXSETSEL();
6631 test_WM_PASTE();
6632 test_EM_STREAMIN();
6633 test_EM_STREAMOUT();
6634 test_EM_StreamIn_Undo();
6635 test_EM_FORMATRANGE();
6636 test_unicode_conversions();
6637 test_EM_GETTEXTLENGTHEX();
6638 test_EM_REPLACESEL(1);
6639 test_EM_REPLACESEL(0);
6640 test_WM_NOTIFY();
6641 test_EM_AUTOURLDETECT();
6642 test_eventMask();
6643 test_undo_coalescing();
6644 test_word_movement();
6645 test_EM_CHARFROMPOS();
6646 test_SETPARAFORMAT();
6647 test_word_wrap();
6648 test_autoscroll();
6649 test_format_rect();
6650 test_WM_GETDLGCODE();
6651 test_zoom();
6653 /* Set the environment variable WINETEST_RICHED20 to keep windows
6654 * responsive and open for 30 seconds. This is useful for debugging.
6656 if (getenv( "WINETEST_RICHED20" )) {
6657 keep_responsive(30);
6660 OleFlushClipboard();
6661 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());