riched20/tests: Fix tests on Win9x by using A-calls.
[wine/multimedia.git] / dlls / riched20 / tests / editor.c
blob9e2f5e41507ebe801addc5c99a4b31f92480f59c
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 todo_wine {
625 /* Fails on builtin because horizontal scrollbar is not being shown */
626 ok((signed short)(LOWORD(result)) < xpos,
627 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
628 (signed short)(LOWORD(result)), xpos);
630 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
632 /* Test around end of text that doesn't end in a newline. */
633 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
634 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
635 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
636 ok(pt.x > 1, "pt.x = %d\n", pt.x);
637 xpos = pt.x;
638 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
639 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
640 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
641 xpos = pt.x;
642 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
643 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
644 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
646 /* Try a negative position. */
647 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
648 ok(pt.x == 1, "pt.x = %d\n", pt.x);
650 DestroyWindow(hwndRichEdit);
653 static void test_EM_SETCHARFORMAT(void)
655 HWND hwndRichEdit = new_richedit(NULL);
656 CHARFORMAT2 cf2;
657 int rc = 0;
658 int tested_effects[] = {
659 CFE_BOLD,
660 CFE_ITALIC,
661 CFE_UNDERLINE,
662 CFE_STRIKEOUT,
663 CFE_PROTECTED,
664 CFE_LINK,
665 CFE_SUBSCRIPT,
666 CFE_SUPERSCRIPT,
669 int i;
670 CHARRANGE cr;
672 /* Invalid flags, CHARFORMAT2 structure blanked out */
673 memset(&cf2, 0, sizeof(cf2));
674 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
675 (LPARAM) &cf2);
676 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
678 /* A valid flag, CHARFORMAT2 structure blanked out */
679 memset(&cf2, 0, sizeof(cf2));
680 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
681 (LPARAM) &cf2);
682 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
684 /* A valid flag, CHARFORMAT2 structure blanked out */
685 memset(&cf2, 0, sizeof(cf2));
686 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
687 (LPARAM) &cf2);
688 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
690 /* A valid flag, CHARFORMAT2 structure blanked out */
691 memset(&cf2, 0, sizeof(cf2));
692 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
693 (LPARAM) &cf2);
694 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
696 /* A valid flag, CHARFORMAT2 structure blanked out */
697 memset(&cf2, 0, sizeof(cf2));
698 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
699 (LPARAM) &cf2);
700 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
702 /* Invalid flags, CHARFORMAT2 structure minimally filled */
703 memset(&cf2, 0, sizeof(cf2));
704 cf2.cbSize = sizeof(CHARFORMAT2);
705 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
706 (LPARAM) &cf2);
707 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
708 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
709 ok(rc == FALSE, "Should not be able to undo here.\n");
710 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
712 /* A valid flag, CHARFORMAT2 structure minimally filled */
713 memset(&cf2, 0, sizeof(cf2));
714 cf2.cbSize = sizeof(CHARFORMAT2);
715 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
716 (LPARAM) &cf2);
717 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
718 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
719 ok(rc == FALSE, "Should not be able to undo here.\n");
720 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
722 /* A valid flag, CHARFORMAT2 structure minimally filled */
723 memset(&cf2, 0, sizeof(cf2));
724 cf2.cbSize = sizeof(CHARFORMAT2);
725 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
726 (LPARAM) &cf2);
727 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
728 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
729 ok(rc == FALSE, "Should not be able to undo here.\n");
730 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
732 /* A valid flag, CHARFORMAT2 structure minimally filled */
733 memset(&cf2, 0, sizeof(cf2));
734 cf2.cbSize = sizeof(CHARFORMAT2);
735 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
736 (LPARAM) &cf2);
737 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
738 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
739 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
740 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
742 /* A valid flag, CHARFORMAT2 structure minimally filled */
743 memset(&cf2, 0, sizeof(cf2));
744 cf2.cbSize = sizeof(CHARFORMAT2);
745 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
746 (LPARAM) &cf2);
747 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
748 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
749 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
750 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
752 cf2.cbSize = sizeof(CHARFORMAT2);
753 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
754 (LPARAM) &cf2);
756 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
757 cf2.cbSize = sizeof(CHARFORMAT2);
758 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
759 (LPARAM) &cf2);
760 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
761 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
763 /* wParam==0 is default char format, does not set modify */
764 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
765 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
766 ok(rc == 0, "Text marked as modified, expected not modified!\n");
767 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
768 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
769 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
770 ok(rc == 0, "Text marked as modified, expected not modified!\n");
772 /* wParam==SCF_SELECTION sets modify if nonempty selection */
773 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
774 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
775 ok(rc == 0, "Text marked as modified, expected not modified!\n");
776 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
777 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
778 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
779 ok(rc == 0, "Text marked as modified, expected not modified!\n");
781 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
782 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
783 ok(rc == 0, "Text marked as modified, expected not modified!\n");
784 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
785 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
786 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
787 ok(rc == 0, "Text marked as modified, expected not modified!\n");
788 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
789 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
790 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
791 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
792 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
794 /* wParam==SCF_ALL sets modify regardless of whether text is present */
795 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
796 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
797 ok(rc == 0, "Text marked as modified, expected not modified!\n");
798 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
799 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
800 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
801 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
803 DestroyWindow(hwndRichEdit);
805 /* EM_GETCHARFORMAT tests */
806 for (i = 0; tested_effects[i]; i++)
808 hwndRichEdit = new_richedit(NULL);
809 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
811 /* Need to set a TrueType font to get consistent CFM_BOLD results */
812 memset(&cf2, 0, sizeof(CHARFORMAT2));
813 cf2.cbSize = sizeof(CHARFORMAT2);
814 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
815 cf2.dwEffects = 0;
816 strcpy(cf2.szFaceName, "Courier New");
817 cf2.wWeight = FW_DONTCARE;
818 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
820 memset(&cf2, 0, sizeof(CHARFORMAT2));
821 cf2.cbSize = sizeof(CHARFORMAT2);
822 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
823 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
824 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
825 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
827 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
828 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
829 ok((cf2.dwEffects & tested_effects[i]) == 0,
830 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
832 memset(&cf2, 0, sizeof(CHARFORMAT2));
833 cf2.cbSize = sizeof(CHARFORMAT2);
834 cf2.dwMask = tested_effects[i];
835 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
836 cf2.dwMask = CFM_SUPERSCRIPT;
837 cf2.dwEffects = tested_effects[i];
838 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
839 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
841 memset(&cf2, 0, sizeof(CHARFORMAT2));
842 cf2.cbSize = sizeof(CHARFORMAT2);
843 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
844 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
845 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
846 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
848 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
849 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
850 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
851 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
853 memset(&cf2, 0, sizeof(CHARFORMAT2));
854 cf2.cbSize = sizeof(CHARFORMAT2);
855 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
856 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
857 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
858 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
860 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
861 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
862 ok((cf2.dwEffects & tested_effects[i]) == 0,
863 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
865 memset(&cf2, 0, sizeof(CHARFORMAT2));
866 cf2.cbSize = sizeof(CHARFORMAT2);
867 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
868 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
869 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
870 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
872 (cf2.dwMask & tested_effects[i]) == 0),
873 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
875 DestroyWindow(hwndRichEdit);
878 for (i = 0; tested_effects[i]; i++)
880 hwndRichEdit = new_richedit(NULL);
881 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
883 /* Need to set a TrueType font to get consistent CFM_BOLD results */
884 memset(&cf2, 0, sizeof(CHARFORMAT2));
885 cf2.cbSize = sizeof(CHARFORMAT2);
886 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
887 cf2.dwEffects = 0;
888 strcpy(cf2.szFaceName, "Courier New");
889 cf2.wWeight = FW_DONTCARE;
890 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
892 memset(&cf2, 0, sizeof(CHARFORMAT2));
893 cf2.cbSize = sizeof(CHARFORMAT2);
894 cf2.dwMask = tested_effects[i];
895 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
896 cf2.dwMask = CFM_SUPERSCRIPT;
897 cf2.dwEffects = tested_effects[i];
898 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
899 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
901 memset(&cf2, 0, sizeof(CHARFORMAT2));
902 cf2.cbSize = sizeof(CHARFORMAT2);
903 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
904 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
905 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
906 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
908 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
909 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
910 ok((cf2.dwEffects & tested_effects[i]) == 0,
911 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
913 memset(&cf2, 0, sizeof(CHARFORMAT2));
914 cf2.cbSize = sizeof(CHARFORMAT2);
915 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
916 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
917 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
918 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
920 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
921 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
922 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
923 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
925 memset(&cf2, 0, sizeof(CHARFORMAT2));
926 cf2.cbSize = sizeof(CHARFORMAT2);
927 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
928 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
929 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
930 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
932 (cf2.dwMask & tested_effects[i]) == 0),
933 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
934 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
935 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
937 DestroyWindow(hwndRichEdit);
940 /* Effects applied on an empty selection should take effect when selection is
941 replaced with text */
942 hwndRichEdit = new_richedit(NULL);
943 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
944 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
946 memset(&cf2, 0, sizeof(CHARFORMAT2));
947 cf2.cbSize = sizeof(CHARFORMAT2);
948 cf2.dwMask = CFM_BOLD;
949 cf2.dwEffects = CFE_BOLD;
950 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
952 /* Selection is now nonempty */
953 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
955 memset(&cf2, 0, sizeof(CHARFORMAT2));
956 cf2.cbSize = sizeof(CHARFORMAT2);
957 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
958 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
960 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
961 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
962 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
963 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
966 /* Set two effects on an empty selection */
967 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
968 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
970 memset(&cf2, 0, sizeof(CHARFORMAT2));
971 cf2.cbSize = sizeof(CHARFORMAT2);
972 cf2.dwMask = CFM_BOLD;
973 cf2.dwEffects = CFE_BOLD;
974 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
975 cf2.dwMask = CFM_ITALIC;
976 cf2.dwEffects = CFE_ITALIC;
977 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
979 /* Selection is now nonempty */
980 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
982 memset(&cf2, 0, sizeof(CHARFORMAT2));
983 cf2.cbSize = sizeof(CHARFORMAT2);
984 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
985 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
987 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
988 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
989 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
990 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
992 /* Setting the (empty) selection to exactly the same place as before should
993 NOT clear the insertion style! */
994 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
995 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
997 memset(&cf2, 0, sizeof(CHARFORMAT2));
998 cf2.cbSize = sizeof(CHARFORMAT2);
999 cf2.dwMask = CFM_BOLD;
1000 cf2.dwEffects = CFE_BOLD;
1001 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1003 /* Empty selection in same place, insert style should NOT be forgotten here. */
1004 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1006 /* Selection is now nonempty */
1007 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1009 memset(&cf2, 0, sizeof(CHARFORMAT2));
1010 cf2.cbSize = sizeof(CHARFORMAT2);
1011 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1012 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1014 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1015 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1016 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1017 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1019 /* Ditto with EM_EXSETSEL */
1020 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1021 cr.cpMin = 2; cr.cpMax = 2;
1022 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1024 memset(&cf2, 0, sizeof(CHARFORMAT2));
1025 cf2.cbSize = sizeof(CHARFORMAT2);
1026 cf2.dwMask = CFM_BOLD;
1027 cf2.dwEffects = CFE_BOLD;
1028 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1030 /* Empty selection in same place, insert style should NOT be forgotten here. */
1031 cr.cpMin = 2; cr.cpMax = 2;
1032 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1034 /* Selection is now nonempty */
1035 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1037 memset(&cf2, 0, sizeof(CHARFORMAT2));
1038 cf2.cbSize = sizeof(CHARFORMAT2);
1039 cr.cpMin = 2; cr.cpMax = 6;
1040 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1041 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1043 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1044 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1045 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1046 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1048 DestroyWindow(hwndRichEdit);
1051 static void test_EM_SETTEXTMODE(void)
1053 HWND hwndRichEdit = new_richedit(NULL);
1054 CHARFORMAT2 cf2, cf2test;
1055 CHARRANGE cr;
1056 int rc = 0;
1058 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1059 /*Insert text into the control*/
1061 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1063 /*Attempt to change the control to plain text mode*/
1064 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1065 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1067 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1068 If rich text is pasted, it should have the same formatting as the rest
1069 of the text in the control*/
1071 /*Italicize the text
1072 *NOTE: If the default text was already italicized, the test will simply
1073 reverse; in other words, it will copy a regular "wine" into a plain
1074 text window that uses an italicized format*/
1075 cf2.cbSize = sizeof(CHARFORMAT2);
1076 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1077 (LPARAM) &cf2);
1079 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1080 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1082 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1083 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1085 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1086 however, SCF_ALL has been implemented*/
1087 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1088 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1090 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1091 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1093 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1095 /*Select the string "wine"*/
1096 cr.cpMin = 0;
1097 cr.cpMax = 4;
1098 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1100 /*Copy the italicized "wine" to the clipboard*/
1101 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1103 /*Reset the formatting to default*/
1104 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1105 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1106 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1108 /*Clear the text in the control*/
1109 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1111 /*Switch to Plain Text Mode*/
1112 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1113 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1115 /*Input "wine" again in normal format*/
1116 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1118 /*Paste the italicized "wine" into the control*/
1119 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1121 /*Select a character from the first "wine" string*/
1122 cr.cpMin = 2;
1123 cr.cpMax = 3;
1124 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1126 /*Retrieve its formatting*/
1127 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1128 (LPARAM) &cf2);
1130 /*Select a character from the second "wine" string*/
1131 cr.cpMin = 5;
1132 cr.cpMax = 6;
1133 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1135 /*Retrieve its formatting*/
1136 cf2test.cbSize = sizeof(CHARFORMAT2);
1137 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1138 (LPARAM) &cf2test);
1140 /*Compare the two formattings*/
1141 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1142 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1143 cf2.dwEffects, cf2test.dwEffects);
1144 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1145 printing "wine" in the current format(normal)
1146 pasting "wine" from the clipboard(italicized)
1147 comparing the two formats(should differ)*/
1149 /*Attempt to switch with text in control*/
1150 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1151 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1153 /*Clear control*/
1154 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1156 /*Switch into Rich Text mode*/
1157 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1158 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1160 /*Print "wine" in normal formatting into the control*/
1161 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1163 /*Paste italicized "wine" into the control*/
1164 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1166 /*Select text from the first "wine" string*/
1167 cr.cpMin = 1;
1168 cr.cpMax = 3;
1169 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1171 /*Retrieve its formatting*/
1172 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1173 (LPARAM) &cf2);
1175 /*Select text from the second "wine" string*/
1176 cr.cpMin = 6;
1177 cr.cpMax = 7;
1178 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1180 /*Retrieve its formatting*/
1181 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1182 (LPARAM) &cf2test);
1184 /*Test that the two formattings are not the same*/
1185 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1186 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1187 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1189 DestroyWindow(hwndRichEdit);
1192 static void test_SETPARAFORMAT(void)
1194 HWND hwndRichEdit = new_richedit(NULL);
1195 PARAFORMAT2 fmt;
1196 HRESULT ret;
1197 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1198 fmt.cbSize = sizeof(PARAFORMAT2);
1199 fmt.dwMask = PFM_ALIGNMENT;
1200 fmt.wAlignment = PFA_LEFT;
1202 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1203 ok(ret != 0, "expected non-zero got %d\n", ret);
1205 fmt.cbSize = sizeof(PARAFORMAT2);
1206 fmt.dwMask = -1;
1207 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1208 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1209 * between richedit different native builds of riched20.dll
1210 * used on different Windows versions. */
1211 ret &= ~PFM_TABLEROWDELIMITER;
1212 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1214 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1215 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1217 DestroyWindow(hwndRichEdit);
1220 static void test_TM_PLAINTEXT(void)
1222 /*Tests plain text properties*/
1224 HWND hwndRichEdit = new_richedit(NULL);
1225 CHARFORMAT2 cf2, cf2test;
1226 CHARRANGE cr;
1227 int rc = 0;
1229 /*Switch to plain text mode*/
1231 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1232 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1234 /*Fill control with text*/
1236 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1238 /*Select some text and bold it*/
1240 cr.cpMin = 10;
1241 cr.cpMax = 20;
1242 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1243 cf2.cbSize = sizeof(CHARFORMAT2);
1244 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1245 (LPARAM) &cf2);
1247 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1248 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1250 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 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_WORD | SCF_SELECTION, (LPARAM) &cf2);
1254 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1256 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1257 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1259 /*Get the formatting of those characters*/
1261 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1263 /*Get the formatting of some other characters*/
1264 cf2test.cbSize = sizeof(CHARFORMAT2);
1265 cr.cpMin = 21;
1266 cr.cpMax = 30;
1267 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1268 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1270 /*Test that they are the same as plain text allows only one formatting*/
1272 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1273 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1274 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1276 /*Fill the control with a "wine" string, which when inserted will be bold*/
1278 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1280 /*Copy the bolded "wine" string*/
1282 cr.cpMin = 0;
1283 cr.cpMax = 4;
1284 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1285 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1287 /*Swap back to rich text*/
1289 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1290 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1292 /*Set the default formatting to bold italics*/
1294 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1295 cf2.dwMask |= CFM_ITALIC;
1296 cf2.dwEffects ^= CFE_ITALIC;
1297 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1298 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1300 /*Set the text in the control to "wine", which will be bold and italicized*/
1302 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1304 /*Paste the plain text "wine" string, which should take the insert
1305 formatting, which at the moment is bold italics*/
1307 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1309 /*Select the first "wine" string and retrieve its formatting*/
1311 cr.cpMin = 1;
1312 cr.cpMax = 3;
1313 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1314 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1316 /*Select the second "wine" string and retrieve its formatting*/
1318 cr.cpMin = 5;
1319 cr.cpMax = 7;
1320 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1321 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1323 /*Compare the two formattings. They should be the same.*/
1325 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1326 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1327 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1328 DestroyWindow(hwndRichEdit);
1331 static void test_WM_GETTEXT(void)
1333 HWND hwndRichEdit = new_richedit(NULL);
1334 static const char text[] = "Hello. My name is RichEdit!";
1335 static const char text2[] = "Hello. My name is RichEdit!\r";
1336 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1337 char buffer[1024] = {0};
1338 int result;
1340 /* Baseline test with normal-sized buffer */
1341 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1342 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1343 ok(result == lstrlen(buffer),
1344 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1345 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1346 result = strcmp(buffer,text);
1347 ok(result == 0,
1348 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1350 /* Test for returned value of WM_GETTEXTLENGTH */
1351 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1352 ok(result == lstrlen(text),
1353 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1354 result, lstrlen(text));
1356 /* Test for behavior in overflow case */
1357 memset(buffer, 0, 1024);
1358 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1359 ok(result == 0 ||
1360 result == lstrlenA(text) - 1, /* XP, win2k3 */
1361 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1362 result = strcmp(buffer,text);
1363 if (result)
1364 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1365 ok(result == 0,
1366 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1368 /* Baseline test with normal-sized buffer and carriage return */
1369 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1370 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1371 ok(result == lstrlen(buffer),
1372 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1373 result = strcmp(buffer,text2_after);
1374 ok(result == 0,
1375 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1377 /* Test for returned value of WM_GETTEXTLENGTH */
1378 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1379 ok(result == lstrlen(text2_after),
1380 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1381 result, lstrlen(text2_after));
1383 /* Test for behavior of CRLF conversion in case of overflow */
1384 memset(buffer, 0, 1024);
1385 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1386 ok(result == 0 ||
1387 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1388 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1389 result = strcmp(buffer,text2);
1390 if (result)
1391 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1392 ok(result == 0,
1393 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1395 DestroyWindow(hwndRichEdit);
1398 static void test_EM_GETTEXTRANGE(void)
1400 HWND hwndRichEdit = new_richedit(NULL);
1401 const char * text1 = "foo bar\r\nfoo bar";
1402 const char * text2 = "foo bar\rfoo bar";
1403 const char * expect = "bar\rfoo";
1404 char buffer[1024] = {0};
1405 LRESULT result;
1406 TEXTRANGEA textRange;
1408 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1410 textRange.lpstrText = buffer;
1411 textRange.chrg.cpMin = 4;
1412 textRange.chrg.cpMax = 11;
1413 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1414 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1415 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1417 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1419 textRange.lpstrText = buffer;
1420 textRange.chrg.cpMin = 4;
1421 textRange.chrg.cpMax = 11;
1422 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1423 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1424 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1426 DestroyWindow(hwndRichEdit);
1429 static void test_EM_GETSELTEXT(void)
1431 HWND hwndRichEdit = new_richedit(NULL);
1432 const char * text1 = "foo bar\r\nfoo bar";
1433 const char * text2 = "foo bar\rfoo bar";
1434 const char * expect = "bar\rfoo";
1435 char buffer[1024] = {0};
1436 LRESULT result;
1438 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1440 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1441 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1442 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1443 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1445 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1447 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1448 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1449 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1450 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1452 DestroyWindow(hwndRichEdit);
1455 /* FIXME: need to test unimplemented options and robustly test wparam */
1456 static void test_EM_SETOPTIONS(void)
1458 HWND hwndRichEdit = new_richedit(NULL);
1459 static const char text[] = "Hello. My name is RichEdit!";
1460 char buffer[1024] = {0};
1462 /* NEGATIVE TESTING - NO OPTIONS SET */
1463 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1464 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1466 /* testing no readonly by sending 'a' to the control*/
1467 SetFocus(hwndRichEdit);
1468 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1469 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1470 ok(buffer[0]=='a',
1471 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1472 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1474 /* READONLY - sending 'a' to the control */
1475 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1476 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1477 SetFocus(hwndRichEdit);
1478 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1479 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1480 ok(buffer[0]==text[0],
1481 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1483 DestroyWindow(hwndRichEdit);
1486 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1488 CHARFORMAT2W text_format;
1489 text_format.cbSize = sizeof(text_format);
1490 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1491 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1492 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1495 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1497 int link_present = 0;
1499 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1500 if (is_url)
1501 { /* control text is url; should get CFE_LINK */
1502 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1504 else
1506 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1510 static HWND new_static_wnd(HWND parent) {
1511 return new_window("Static", 0, parent);
1514 static void test_EM_AUTOURLDETECT(void)
1516 /* DO NOT change the properties of the first two elements. To shorten the
1517 tests, all tests after WM_SETTEXT test just the first two elements -
1518 one non-URL and one URL */
1519 struct urls_s {
1520 const char *text;
1521 int is_url;
1522 } urls[12] = {
1523 {"winehq.org", 0},
1524 {"http://www.winehq.org", 1},
1525 {"http//winehq.org", 0},
1526 {"ww.winehq.org", 0},
1527 {"www.winehq.org", 1},
1528 {"ftp://192.168.1.1", 1},
1529 {"ftp//192.168.1.1", 0},
1530 {"mailto:your@email.com", 1},
1531 {"prospero:prosperoserver", 1},
1532 {"telnet:test", 1},
1533 {"news:newserver", 1},
1534 {"wais:waisserver", 1}
1537 int i, j;
1538 int urlRet=-1;
1539 HWND hwndRichEdit, parent;
1541 /* All of the following should cause the URL to be detected */
1542 const char * templates_delim[] = {
1543 "This is some text with X on it",
1544 "This is some text with (X) on it",
1545 "This is some text with X\r on it",
1546 "This is some text with ---X--- on it",
1547 "This is some text with \"X\" on it",
1548 "This is some text with 'X' on it",
1549 "This is some text with 'X' on it",
1550 "This is some text with :X: on it",
1552 "This text ends with X",
1554 "This is some text with X) on it",
1555 "This is some text with X--- on it",
1556 "This is some text with X\" on it",
1557 "This is some text with X' on it",
1558 "This is some text with X: on it",
1560 "This is some text with (X on it",
1561 "This is some text with \rX on it",
1562 "This is some text with ---X on it",
1563 "This is some text with \"X on it",
1564 "This is some text with 'X on it",
1565 "This is some text with :X on it",
1567 /* None of these should cause the URL to be detected */
1568 const char * templates_non_delim[] = {
1569 "This is some text with |X| on it",
1570 "This is some text with *X* on it",
1571 "This is some text with /X/ on it",
1572 "This is some text with +X+ on it",
1573 "This is some text with %X% on it",
1574 "This is some text with #X# on it",
1575 "This is some text with @X@ on it",
1576 "This is some text with \\X\\ on it",
1577 "This is some text with |X on it",
1578 "This is some text with *X on it",
1579 "This is some text with /X on it",
1580 "This is some text with +X on it",
1581 "This is some text with %X on it",
1582 "This is some text with #X on it",
1583 "This is some text with @X on it",
1584 "This is some text with \\X on it",
1586 /* All of these cause the URL detection to be extended by one more byte,
1587 thus demonstrating that the tested character is considered as part
1588 of the URL. */
1589 const char * templates_xten_delim[] = {
1590 "This is some text with X| on it",
1591 "This is some text with X* on it",
1592 "This is some text with X/ on it",
1593 "This is some text with X+ on it",
1594 "This is some text with X% on it",
1595 "This is some text with X# on it",
1596 "This is some text with X@ on it",
1597 "This is some text with X\\ on it",
1599 char buffer[1024];
1601 parent = new_static_wnd(NULL);
1602 hwndRichEdit = new_richedit(parent);
1603 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1604 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1605 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1606 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1607 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1608 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1609 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1610 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1611 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1612 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1613 /* for each url, check the text to see if CFE_LINK effect is present */
1614 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1616 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1617 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1618 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1620 /* Link detection should happen immediately upon WM_SETTEXT */
1621 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1622 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1623 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1625 DestroyWindow(hwndRichEdit);
1627 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1628 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1629 hwndRichEdit = new_richedit(parent);
1631 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1632 char * at_pos;
1633 int at_offset;
1634 int end_offset;
1636 at_pos = strchr(templates_delim[j], 'X');
1637 at_offset = at_pos - templates_delim[j];
1638 strncpy(buffer, templates_delim[j], at_offset);
1639 buffer[at_offset] = '\0';
1640 strcat(buffer, urls[i].text);
1641 strcat(buffer, templates_delim[j] + at_offset + 1);
1642 end_offset = at_offset + strlen(urls[i].text);
1644 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1645 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1647 /* This assumes no templates start with the URL itself, and that they
1648 have at least two characters before the URL text */
1649 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1650 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1651 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1652 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1653 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1654 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1656 if (urls[i].is_url)
1658 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1659 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1660 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1661 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1663 else
1665 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1666 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1667 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1668 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1670 if (buffer[end_offset] != '\0')
1672 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1673 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1674 if (buffer[end_offset +1] != '\0')
1676 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1677 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1682 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1683 char * at_pos;
1684 int at_offset;
1685 int end_offset;
1687 at_pos = strchr(templates_non_delim[j], 'X');
1688 at_offset = at_pos - templates_non_delim[j];
1689 strncpy(buffer, templates_non_delim[j], at_offset);
1690 buffer[at_offset] = '\0';
1691 strcat(buffer, urls[i].text);
1692 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1693 end_offset = at_offset + strlen(urls[i].text);
1695 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1696 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1698 /* This assumes no templates start with the URL itself, and that they
1699 have at least two characters before the URL text */
1700 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1701 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1702 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1703 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1704 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1705 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1707 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1708 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1709 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1710 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1711 if (buffer[end_offset] != '\0')
1713 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1714 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1715 if (buffer[end_offset +1] != '\0')
1717 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1718 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1723 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1724 char * at_pos;
1725 int at_offset;
1726 int end_offset;
1728 at_pos = strchr(templates_xten_delim[j], 'X');
1729 at_offset = at_pos - templates_xten_delim[j];
1730 strncpy(buffer, templates_xten_delim[j], at_offset);
1731 buffer[at_offset] = '\0';
1732 strcat(buffer, urls[i].text);
1733 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1734 end_offset = at_offset + strlen(urls[i].text);
1736 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1737 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1739 /* This assumes no templates start with the URL itself, and that they
1740 have at least two characters before the URL text */
1741 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1742 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1743 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1744 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1745 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1746 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1748 if (urls[i].is_url)
1750 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1751 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1752 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1753 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1754 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1755 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1757 else
1759 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1760 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1761 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1762 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1763 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1764 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1766 if (buffer[end_offset +1] != '\0')
1768 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1769 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1770 if (buffer[end_offset +2] != '\0')
1772 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1773 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1778 DestroyWindow(hwndRichEdit);
1779 hwndRichEdit = NULL;
1782 /* Test detection of URLs within normal text - WM_CHAR case. */
1783 /* Test only the first two URL examples for brevity */
1784 for (i = 0; i < 2; i++) {
1785 hwndRichEdit = new_richedit(parent);
1787 /* Also for brevity, test only the first three delimiters */
1788 for (j = 0; j < 3; j++) {
1789 char * at_pos;
1790 int at_offset;
1791 int end_offset;
1792 int u, v;
1794 at_pos = strchr(templates_delim[j], 'X');
1795 at_offset = at_pos - templates_delim[j];
1796 end_offset = at_offset + strlen(urls[i].text);
1798 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1799 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1800 for (u = 0; templates_delim[j][u]; u++) {
1801 if (templates_delim[j][u] == '\r') {
1802 simulate_typing_characters(hwndRichEdit, "\r");
1803 } else if (templates_delim[j][u] != 'X') {
1804 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1805 } else {
1806 for (v = 0; urls[i].text[v]; v++) {
1807 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1811 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1813 /* This assumes no templates start with the URL itself, and that they
1814 have at least two characters before the URL text */
1815 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1816 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1817 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1818 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1819 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1820 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1822 if (urls[i].is_url)
1824 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1825 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1826 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1827 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1829 else
1831 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1832 "CFE_LINK incorrectly 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 incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1836 if (buffer[end_offset] != '\0')
1838 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1839 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1840 if (buffer[end_offset +1] != '\0')
1842 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1843 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1847 /* The following will insert a paragraph break after the first character
1848 of the URL candidate, thus breaking the URL. It is expected that the
1849 CFE_LINK attribute should break across both pieces of the URL */
1850 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1851 simulate_typing_characters(hwndRichEdit, "\r");
1852 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1854 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1855 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1856 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1857 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1858 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1859 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1861 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1862 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1863 /* end_offset moved because of paragraph break */
1864 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1865 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1866 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1867 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1869 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1870 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1871 if (buffer[end_offset +2] != '\0')
1873 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1874 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1878 /* The following will remove the just-inserted paragraph break, thus
1879 restoring the URL */
1880 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1881 simulate_typing_characters(hwndRichEdit, "\b");
1882 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1884 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1885 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1886 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1887 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1888 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1889 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1891 if (urls[i].is_url)
1893 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1894 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1895 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1896 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1898 else
1900 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1901 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1902 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1903 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1905 if (buffer[end_offset] != '\0')
1907 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1908 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1909 if (buffer[end_offset +1] != '\0')
1911 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1912 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1916 DestroyWindow(hwndRichEdit);
1917 hwndRichEdit = NULL;
1920 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1921 /* Test just the first two URL examples for brevity */
1922 for (i = 0; i < 2; i++) {
1923 SETTEXTEX st;
1925 hwndRichEdit = new_richedit(parent);
1927 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1928 be detected:
1929 1) Set entire text, a la WM_SETTEXT
1930 2) Set a selection of the text to the URL
1931 3) Set a portion of the text at a time, which eventually results in
1932 an URL
1933 All of them should give equivalent results
1936 /* Set entire text in one go, like WM_SETTEXT */
1937 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1938 char * at_pos;
1939 int at_offset;
1940 int end_offset;
1942 st.codepage = CP_ACP;
1943 st.flags = ST_DEFAULT;
1945 at_pos = strchr(templates_delim[j], 'X');
1946 at_offset = at_pos - templates_delim[j];
1947 strncpy(buffer, templates_delim[j], at_offset);
1948 buffer[at_offset] = '\0';
1949 strcat(buffer, urls[i].text);
1950 strcat(buffer, templates_delim[j] + at_offset + 1);
1951 end_offset = at_offset + strlen(urls[i].text);
1953 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1954 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1956 /* This assumes no templates start with the URL itself, and that they
1957 have at least two characters before the URL text */
1958 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1959 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1960 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1961 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1962 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1963 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1965 if (urls[i].is_url)
1967 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1968 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1969 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1970 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1972 else
1974 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1975 "CFE_LINK incorrectly 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 incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1979 if (buffer[end_offset] != '\0')
1981 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1982 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1983 if (buffer[end_offset +1] != '\0')
1985 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1986 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1991 /* Set selection with X to the URL */
1992 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1993 char * at_pos;
1994 int at_offset;
1995 int end_offset;
1997 at_pos = strchr(templates_delim[j], 'X');
1998 at_offset = at_pos - templates_delim[j];
1999 end_offset = at_offset + strlen(urls[i].text);
2001 st.codepage = CP_ACP;
2002 st.flags = ST_DEFAULT;
2003 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2004 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2005 st.flags = ST_SELECTION;
2006 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2007 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2008 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2010 /* This assumes no templates start with the URL itself, and that they
2011 have at least two characters before the URL text */
2012 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2013 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2014 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2015 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2016 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2017 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2019 if (urls[i].is_url)
2021 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2022 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2023 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2024 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2026 else
2028 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2029 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2030 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2031 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2033 if (buffer[end_offset] != '\0')
2035 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2036 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2037 if (buffer[end_offset +1] != '\0')
2039 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2040 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2045 /* Set selection with X to the first character of the URL, then the rest */
2046 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2047 char * at_pos;
2048 int at_offset;
2049 int end_offset;
2051 at_pos = strchr(templates_delim[j], 'X');
2052 at_offset = at_pos - templates_delim[j];
2053 end_offset = at_offset + strlen(urls[i].text);
2055 strcpy(buffer, "YY");
2056 buffer[0] = urls[i].text[0];
2058 st.codepage = CP_ACP;
2059 st.flags = ST_DEFAULT;
2060 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2061 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2062 st.flags = ST_SELECTION;
2063 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2064 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2065 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2066 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2067 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2069 /* This assumes no templates start with the URL itself, and that they
2070 have at least two characters before the URL text */
2071 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2072 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2073 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2074 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2075 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2076 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2078 if (urls[i].is_url)
2080 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2081 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2082 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2083 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2085 else
2087 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2088 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2089 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2090 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2092 if (buffer[end_offset] != '\0')
2094 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2095 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2096 if (buffer[end_offset +1] != '\0')
2098 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2099 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2104 DestroyWindow(hwndRichEdit);
2105 hwndRichEdit = NULL;
2108 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2109 /* Test just the first two URL examples for brevity */
2110 for (i = 0; i < 2; i++) {
2111 hwndRichEdit = new_richedit(parent);
2113 /* Set selection with X to the URL */
2114 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2115 char * at_pos;
2116 int at_offset;
2117 int end_offset;
2119 at_pos = strchr(templates_delim[j], 'X');
2120 at_offset = at_pos - templates_delim[j];
2121 end_offset = at_offset + strlen(urls[i].text);
2123 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2124 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2125 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2126 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2127 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2129 /* This assumes no templates start with the URL itself, and that they
2130 have at least two characters before the URL text */
2131 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2132 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2133 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2134 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2135 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2136 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2138 if (urls[i].is_url)
2140 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2141 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2142 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2143 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2145 else
2147 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2148 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2149 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2150 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2152 if (buffer[end_offset] != '\0')
2154 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2155 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2156 if (buffer[end_offset +1] != '\0')
2158 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2159 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2164 /* Set selection with X to the first character of the URL, then the rest */
2165 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2166 char * at_pos;
2167 int at_offset;
2168 int end_offset;
2170 at_pos = strchr(templates_delim[j], 'X');
2171 at_offset = at_pos - templates_delim[j];
2172 end_offset = at_offset + strlen(urls[i].text);
2174 strcpy(buffer, "YY");
2175 buffer[0] = urls[i].text[0];
2177 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2178 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2179 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2180 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2181 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2182 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2183 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2185 /* This assumes no templates start with the URL itself, and that they
2186 have at least two characters before the URL text */
2187 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2188 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2189 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2190 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2191 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2192 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2194 if (urls[i].is_url)
2196 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2197 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2198 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2199 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2201 else
2203 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2204 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2205 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2206 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2208 if (buffer[end_offset] != '\0')
2210 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2211 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2212 if (buffer[end_offset +1] != '\0')
2214 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2215 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2220 DestroyWindow(hwndRichEdit);
2221 hwndRichEdit = NULL;
2224 DestroyWindow(parent);
2227 static void test_EM_SCROLL(void)
2229 int i, j;
2230 int r; /* return value */
2231 int expr; /* expected return value */
2232 HWND hwndRichEdit = new_richedit(NULL);
2233 int y_before, y_after; /* units of lines of text */
2235 /* test a richedit box containing a single line of text */
2236 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2237 expr = 0x00010000;
2238 for (i = 0; i < 4; i++) {
2239 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2241 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2242 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2243 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2244 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2245 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2246 "(i == %d)\n", y_after, i);
2250 * test a richedit box that will scroll. There are two general
2251 * cases: the case without any long lines and the case with a long
2252 * line.
2254 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2255 if (i == 0)
2256 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2257 else
2258 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2259 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2260 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2261 "LONG LINE \nb\nc\nd\ne");
2262 for (j = 0; j < 12; j++) /* reset scroll position to top */
2263 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2265 /* get first visible line */
2266 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2267 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2269 /* get new current first visible line */
2270 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2272 ok(((r & 0xffffff00) == 0x00010000) &&
2273 ((r & 0x000000ff) != 0x00000000),
2274 "EM_SCROLL page down didn't scroll by a small positive number of "
2275 "lines (r == 0x%08x)\n", r);
2276 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2277 "(line %d scrolled to line %d\n", y_before, y_after);
2279 y_before = y_after;
2281 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2282 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2283 ok(((r & 0xffffff00) == 0x0001ff00),
2284 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2285 "(r == 0x%08x)\n", r);
2286 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2287 "%d scrolled to line %d\n", y_before, y_after);
2289 y_before = y_after;
2291 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2293 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2295 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2296 "(r == 0x%08x)\n", r);
2297 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2298 "1 line (%d scrolled to %d)\n", y_before, y_after);
2300 y_before = y_after;
2302 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2304 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2306 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2307 "(r == 0x%08x)\n", r);
2308 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2309 "line (%d scrolled to %d)\n", y_before, y_after);
2311 y_before = y_after;
2313 r = SendMessage(hwndRichEdit, EM_SCROLL,
2314 SB_LINEUP, 0); /* lineup beyond top */
2316 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2318 ok(r == 0x00010000,
2319 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2320 ok(y_before == y_after,
2321 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2323 y_before = y_after;
2325 r = SendMessage(hwndRichEdit, EM_SCROLL,
2326 SB_PAGEUP, 0);/*page up beyond top */
2328 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2330 ok(r == 0x00010000,
2331 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2332 ok(y_before == y_after,
2333 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2335 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2336 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2337 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2338 r = SendMessage(hwndRichEdit, EM_SCROLL,
2339 SB_PAGEDOWN, 0); /* page down beyond bot */
2340 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2342 ok(r == 0x00010000,
2343 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2344 ok(y_before == y_after,
2345 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2346 y_before, y_after);
2348 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2349 SendMessage(hwndRichEdit, EM_SCROLL,
2350 SB_LINEDOWN, 0); /* line down beyond bot */
2351 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2353 ok(r == 0x00010000,
2354 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2355 ok(y_before == y_after,
2356 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2357 y_before, y_after);
2359 DestroyWindow(hwndRichEdit);
2362 unsigned int recursionLevel = 0;
2363 unsigned int WM_SIZE_recursionLevel = 0;
2364 BOOL bailedOutOfRecursion = FALSE;
2365 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2367 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2369 LRESULT r;
2371 if (bailedOutOfRecursion) return 0;
2372 if (recursionLevel >= 32) {
2373 bailedOutOfRecursion = TRUE;
2374 return 0;
2377 recursionLevel++;
2378 switch (message) {
2379 case WM_SIZE:
2380 WM_SIZE_recursionLevel++;
2381 r = richeditProc(hwnd, message, wParam, lParam);
2382 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2383 ShowScrollBar(hwnd, SB_VERT, TRUE);
2384 WM_SIZE_recursionLevel--;
2385 break;
2386 default:
2387 r = richeditProc(hwnd, message, wParam, lParam);
2388 break;
2390 recursionLevel--;
2391 return r;
2394 static void test_scrollbar_visibility(void)
2396 HWND hwndRichEdit;
2397 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2398 SCROLLINFO si;
2399 WNDCLASSA cls;
2400 BOOL r;
2402 /* These tests show that richedit should temporarily refrain from automatically
2403 hiding or showing its scrollbars (vertical at least) when an explicit request
2404 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2405 Some applications depend on forced showing (when otherwise richedit would
2406 hide the vertical scrollbar) and are thrown on an endless recursive loop
2407 if richedit auto-hides the scrollbar again. Apparently they never heard of
2408 the ES_DISABLENOSCROLL style... */
2410 hwndRichEdit = new_richedit(NULL);
2412 /* Test default scrollbar visibility behavior */
2413 memset(&si, 0, sizeof(si));
2414 si.cbSize = sizeof(si);
2415 si.fMask = SIF_PAGE | SIF_RANGE;
2416 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2417 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2418 "Vertical scrollbar is visible, should be invisible.\n");
2419 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2420 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2421 si.nPage, si.nMin, si.nMax);
2423 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2424 memset(&si, 0, sizeof(si));
2425 si.cbSize = sizeof(si);
2426 si.fMask = SIF_PAGE | SIF_RANGE;
2427 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2428 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2429 "Vertical scrollbar is visible, should be invisible.\n");
2430 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2431 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2432 si.nPage, si.nMin, si.nMax);
2434 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2435 memset(&si, 0, sizeof(si));
2436 si.cbSize = sizeof(si);
2437 si.fMask = SIF_PAGE | SIF_RANGE;
2438 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2439 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2440 "Vertical scrollbar is invisible, should be visible.\n");
2441 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2442 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2443 si.nPage, si.nMin, si.nMax);
2445 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2446 even though it hides the scrollbar */
2447 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2448 memset(&si, 0, sizeof(si));
2449 si.cbSize = sizeof(si);
2450 si.fMask = SIF_PAGE | SIF_RANGE;
2451 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2452 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2453 "Vertical scrollbar is visible, should be invisible.\n");
2454 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2455 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2456 si.nPage, si.nMin, si.nMax);
2458 /* Setting non-scrolling text again does *not* reset scrollbar range */
2459 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2460 memset(&si, 0, sizeof(si));
2461 si.cbSize = sizeof(si);
2462 si.fMask = SIF_PAGE | SIF_RANGE;
2463 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2464 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2465 "Vertical scrollbar is visible, should be invisible.\n");
2466 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2467 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2468 si.nPage, si.nMin, si.nMax);
2470 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2471 memset(&si, 0, sizeof(si));
2472 si.cbSize = sizeof(si);
2473 si.fMask = SIF_PAGE | SIF_RANGE;
2474 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2475 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2476 "Vertical scrollbar is visible, should be invisible.\n");
2477 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2478 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2479 si.nPage, si.nMin, si.nMax);
2481 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2482 memset(&si, 0, sizeof(si));
2483 si.cbSize = sizeof(si);
2484 si.fMask = SIF_PAGE | SIF_RANGE;
2485 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2486 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2487 "Vertical scrollbar is visible, should be invisible.\n");
2488 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2489 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2490 si.nPage, si.nMin, si.nMax);
2492 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2493 memset(&si, 0, sizeof(si));
2494 si.cbSize = sizeof(si);
2495 si.fMask = SIF_PAGE | SIF_RANGE;
2496 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2497 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2498 "Vertical scrollbar is visible, should be invisible.\n");
2499 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2500 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2501 si.nPage, si.nMin, si.nMax);
2503 DestroyWindow(hwndRichEdit);
2505 /* Test again, with ES_DISABLENOSCROLL style */
2506 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2508 /* Test default scrollbar visibility behavior */
2509 memset(&si, 0, sizeof(si));
2510 si.cbSize = sizeof(si);
2511 si.fMask = SIF_PAGE | SIF_RANGE;
2512 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2513 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2514 "Vertical scrollbar is invisible, should be visible.\n");
2515 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2516 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2517 si.nPage, si.nMin, si.nMax);
2519 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2520 memset(&si, 0, sizeof(si));
2521 si.cbSize = sizeof(si);
2522 si.fMask = SIF_PAGE | SIF_RANGE;
2523 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2524 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2525 "Vertical scrollbar is invisible, should be visible.\n");
2526 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2527 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2528 si.nPage, si.nMin, si.nMax);
2530 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2531 memset(&si, 0, sizeof(si));
2532 si.cbSize = sizeof(si);
2533 si.fMask = SIF_PAGE | SIF_RANGE;
2534 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2535 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2536 "Vertical scrollbar is invisible, should be visible.\n");
2537 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2538 "reported page/range is %d (%d..%d)\n",
2539 si.nPage, si.nMin, si.nMax);
2541 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2542 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2543 memset(&si, 0, sizeof(si));
2544 si.cbSize = sizeof(si);
2545 si.fMask = SIF_PAGE | SIF_RANGE;
2546 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2547 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2548 "Vertical scrollbar is invisible, should be visible.\n");
2549 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2550 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2551 si.nPage, si.nMin, si.nMax);
2553 /* Setting non-scrolling text again does *not* reset scrollbar range */
2554 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2555 memset(&si, 0, sizeof(si));
2556 si.cbSize = sizeof(si);
2557 si.fMask = SIF_PAGE | SIF_RANGE;
2558 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2559 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2560 "Vertical scrollbar is invisible, should be visible.\n");
2561 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2562 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2563 si.nPage, si.nMin, si.nMax);
2565 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2566 memset(&si, 0, sizeof(si));
2567 si.cbSize = sizeof(si);
2568 si.fMask = SIF_PAGE | SIF_RANGE;
2569 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2570 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2571 "Vertical scrollbar is invisible, should be visible.\n");
2572 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2573 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2574 si.nPage, si.nMin, si.nMax);
2576 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2577 memset(&si, 0, sizeof(si));
2578 si.cbSize = sizeof(si);
2579 si.fMask = SIF_PAGE | SIF_RANGE;
2580 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2581 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2582 "Vertical scrollbar is invisible, should be visible.\n");
2583 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2584 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2585 si.nPage, si.nMin, si.nMax);
2587 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2588 memset(&si, 0, sizeof(si));
2589 si.cbSize = sizeof(si);
2590 si.fMask = SIF_PAGE | SIF_RANGE;
2591 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2592 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2593 "Vertical scrollbar is invisible, should be visible.\n");
2594 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2595 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2596 si.nPage, si.nMin, si.nMax);
2598 DestroyWindow(hwndRichEdit);
2600 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2601 hwndRichEdit = new_richedit(NULL);
2603 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2604 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2605 memset(&si, 0, sizeof(si));
2606 si.cbSize = sizeof(si);
2607 si.fMask = SIF_PAGE | SIF_RANGE;
2608 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2609 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2610 "Vertical scrollbar is invisible, should be visible.\n");
2611 todo_wine {
2612 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2613 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2614 si.nPage, si.nMin, si.nMax);
2617 /* Ditto, see above */
2618 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2619 memset(&si, 0, sizeof(si));
2620 si.cbSize = sizeof(si);
2621 si.fMask = SIF_PAGE | SIF_RANGE;
2622 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2623 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2624 "Vertical scrollbar is invisible, should be visible.\n");
2625 todo_wine {
2626 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2627 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2628 si.nPage, si.nMin, si.nMax);
2631 /* Ditto, see above */
2632 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2633 memset(&si, 0, sizeof(si));
2634 si.cbSize = sizeof(si);
2635 si.fMask = SIF_PAGE | SIF_RANGE;
2636 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2637 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2638 "Vertical scrollbar is invisible, should be visible.\n");
2639 todo_wine {
2640 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2641 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2642 si.nPage, si.nMin, si.nMax);
2645 /* Ditto, see above */
2646 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
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 todo_wine {
2654 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2655 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2656 si.nPage, si.nMin, si.nMax);
2659 /* Ditto, see above */
2660 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2661 memset(&si, 0, sizeof(si));
2662 si.cbSize = sizeof(si);
2663 si.fMask = SIF_PAGE | SIF_RANGE;
2664 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2665 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2666 "Vertical scrollbar is invisible, should be visible.\n");
2667 todo_wine {
2668 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2669 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2670 si.nPage, si.nMin, si.nMax);
2673 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2674 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2675 memset(&si, 0, sizeof(si));
2676 si.cbSize = sizeof(si);
2677 si.fMask = SIF_PAGE | SIF_RANGE;
2678 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2679 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2680 "Vertical scrollbar is visible, should be invisible.\n");
2681 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2682 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2683 si.nPage, si.nMin, si.nMax);
2685 DestroyWindow(hwndRichEdit);
2687 hwndRichEdit = new_richedit(NULL);
2689 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2690 memset(&si, 0, sizeof(si));
2691 si.cbSize = sizeof(si);
2692 si.fMask = SIF_PAGE | SIF_RANGE;
2693 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2694 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2695 "Vertical scrollbar is visible, should be invisible.\n");
2696 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2697 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2698 si.nPage, si.nMin, si.nMax);
2700 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2701 memset(&si, 0, sizeof(si));
2702 si.cbSize = sizeof(si);
2703 si.fMask = SIF_PAGE | SIF_RANGE;
2704 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2705 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2706 "Vertical scrollbar is visible, should be invisible.\n");
2707 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2708 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2709 si.nPage, si.nMin, si.nMax);
2711 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2712 memset(&si, 0, sizeof(si));
2713 si.cbSize = sizeof(si);
2714 si.fMask = SIF_PAGE | SIF_RANGE;
2715 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2716 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2717 "Vertical scrollbar is visible, should be invisible.\n");
2718 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2719 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2720 si.nPage, si.nMin, si.nMax);
2722 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2723 memset(&si, 0, sizeof(si));
2724 si.cbSize = sizeof(si);
2725 si.fMask = SIF_PAGE | SIF_RANGE;
2726 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2727 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2728 "Vertical scrollbar is visible, should be invisible.\n");
2729 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2730 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2731 si.nPage, si.nMin, si.nMax);
2733 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2734 memset(&si, 0, sizeof(si));
2735 si.cbSize = sizeof(si);
2736 si.fMask = SIF_PAGE | SIF_RANGE;
2737 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2738 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2739 "Vertical scrollbar is invisible, should be visible.\n");
2740 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2741 "reported page/range is %d (%d..%d)\n",
2742 si.nPage, si.nMin, si.nMax);
2744 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2745 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2746 memset(&si, 0, sizeof(si));
2747 si.cbSize = sizeof(si);
2748 si.fMask = SIF_PAGE | SIF_RANGE;
2749 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2750 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2751 "Vertical scrollbar is visible, should be invisible.\n");
2752 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2753 "reported page/range is %d (%d..%d)\n",
2754 si.nPage, si.nMin, si.nMax);
2756 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2757 memset(&si, 0, sizeof(si));
2758 si.cbSize = sizeof(si);
2759 si.fMask = SIF_PAGE | SIF_RANGE;
2760 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2761 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2762 "Vertical scrollbar is visible, should be invisible.\n");
2763 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2764 "reported page/range is %d (%d..%d)\n",
2765 si.nPage, si.nMin, si.nMax);
2767 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2768 EM_SCROLL will make visible any forcefully invisible scrollbar */
2769 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2770 memset(&si, 0, sizeof(si));
2771 si.cbSize = sizeof(si);
2772 si.fMask = SIF_PAGE | SIF_RANGE;
2773 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2774 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2775 "Vertical scrollbar is invisible, should be visible.\n");
2776 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2777 "reported page/range is %d (%d..%d)\n",
2778 si.nPage, si.nMin, si.nMax);
2780 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2781 memset(&si, 0, sizeof(si));
2782 si.cbSize = sizeof(si);
2783 si.fMask = SIF_PAGE | SIF_RANGE;
2784 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2785 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2786 "Vertical scrollbar is visible, should be invisible.\n");
2787 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2788 "reported page/range is %d (%d..%d)\n",
2789 si.nPage, si.nMin, si.nMax);
2791 /* Again, EM_SCROLL, with SB_LINEUP */
2792 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
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 invisible, should be visible.\n");
2799 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2800 "reported page/range is %d (%d..%d)\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,
2811 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\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 DestroyWindow(hwndRichEdit);
2828 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2829 hwndRichEdit = new_richedit(NULL);
2831 #define ENABLE_WS_VSCROLL(hwnd) \
2832 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2833 #define DISABLE_WS_VSCROLL(hwnd) \
2834 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2836 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2837 ENABLE_WS_VSCROLL(hwndRichEdit);
2838 memset(&si, 0, sizeof(si));
2839 si.cbSize = sizeof(si);
2840 si.fMask = SIF_PAGE | SIF_RANGE;
2841 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2842 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2843 "Vertical scrollbar is invisible, should be visible.\n");
2844 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2845 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2846 si.nPage, si.nMin, si.nMax);
2848 /* Ditto, see above */
2849 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2850 memset(&si, 0, sizeof(si));
2851 si.cbSize = sizeof(si);
2852 si.fMask = SIF_PAGE | SIF_RANGE;
2853 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2854 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2855 "Vertical scrollbar is invisible, should be visible.\n");
2856 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2857 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2858 si.nPage, si.nMin, si.nMax);
2860 /* Ditto, see above */
2861 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
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 invisible, should be visible.\n");
2868 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2869 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2870 si.nPage, si.nMin, si.nMax);
2872 /* Ditto, see above */
2873 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
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 || si.nMax == 100),
2881 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2882 si.nPage, si.nMin, si.nMax);
2884 /* Ditto, see above */
2885 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2886 memset(&si, 0, sizeof(si));
2887 si.cbSize = sizeof(si);
2888 si.fMask = SIF_PAGE | SIF_RANGE;
2889 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2890 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2891 "Vertical scrollbar is invisible, should be visible.\n");
2892 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2893 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2894 si.nPage, si.nMin, si.nMax);
2896 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2897 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2898 memset(&si, 0, sizeof(si));
2899 si.cbSize = sizeof(si);
2900 si.fMask = SIF_PAGE | SIF_RANGE;
2901 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2902 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2903 "Vertical scrollbar is visible, should be invisible.\n");
2904 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2905 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2906 si.nPage, si.nMin, si.nMax);
2908 DestroyWindow(hwndRichEdit);
2910 hwndRichEdit = new_richedit(NULL);
2912 DISABLE_WS_VSCROLL(hwndRichEdit);
2913 memset(&si, 0, sizeof(si));
2914 si.cbSize = sizeof(si);
2915 si.fMask = SIF_PAGE | SIF_RANGE;
2916 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2917 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2918 "Vertical scrollbar is visible, should be invisible.\n");
2919 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2920 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2921 si.nPage, si.nMin, si.nMax);
2923 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2924 memset(&si, 0, sizeof(si));
2925 si.cbSize = sizeof(si);
2926 si.fMask = SIF_PAGE | SIF_RANGE;
2927 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2928 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2929 "Vertical scrollbar is visible, should be invisible.\n");
2930 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2931 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2932 si.nPage, si.nMin, si.nMax);
2934 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2935 memset(&si, 0, sizeof(si));
2936 si.cbSize = sizeof(si);
2937 si.fMask = SIF_PAGE | SIF_RANGE;
2938 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2939 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2940 "Vertical scrollbar is visible, should be invisible.\n");
2941 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2942 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2943 si.nPage, si.nMin, si.nMax);
2945 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2946 memset(&si, 0, sizeof(si));
2947 si.cbSize = sizeof(si);
2948 si.fMask = SIF_PAGE | SIF_RANGE;
2949 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2950 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2951 "Vertical scrollbar is visible, should be invisible.\n");
2952 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2953 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2954 si.nPage, si.nMin, si.nMax);
2956 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2957 memset(&si, 0, sizeof(si));
2958 si.cbSize = sizeof(si);
2959 si.fMask = SIF_PAGE | SIF_RANGE;
2960 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2961 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2962 "Vertical scrollbar is invisible, should be visible.\n");
2963 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2964 "reported page/range is %d (%d..%d)\n",
2965 si.nPage, si.nMin, si.nMax);
2967 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2968 DISABLE_WS_VSCROLL(hwndRichEdit);
2969 memset(&si, 0, sizeof(si));
2970 si.cbSize = sizeof(si);
2971 si.fMask = SIF_PAGE | SIF_RANGE;
2972 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2973 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2974 "Vertical scrollbar is visible, should be invisible.\n");
2975 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2976 "reported page/range is %d (%d..%d)\n",
2977 si.nPage, si.nMin, si.nMax);
2979 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2980 memset(&si, 0, sizeof(si));
2981 si.cbSize = sizeof(si);
2982 si.fMask = SIF_PAGE | SIF_RANGE;
2983 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2984 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2985 "Vertical scrollbar is visible, should be invisible.\n");
2986 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2987 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2988 si.nPage, si.nMin, si.nMax);
2990 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2991 memset(&si, 0, sizeof(si));
2992 si.cbSize = sizeof(si);
2993 si.fMask = SIF_PAGE | SIF_RANGE;
2994 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2995 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2996 "Vertical scrollbar is invisible, should be visible.\n");
2997 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2998 "reported page/range is %d (%d..%d)\n",
2999 si.nPage, si.nMin, si.nMax);
3001 DISABLE_WS_VSCROLL(hwndRichEdit);
3002 memset(&si, 0, sizeof(si));
3003 si.cbSize = sizeof(si);
3004 si.fMask = SIF_PAGE | SIF_RANGE;
3005 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3006 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3007 "Vertical scrollbar is visible, should be invisible.\n");
3008 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3009 "reported page/range is %d (%d..%d)\n",
3010 si.nPage, si.nMin, si.nMax);
3012 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3013 EM_SCROLL will make visible any forcefully invisible scrollbar */
3014 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3015 memset(&si, 0, sizeof(si));
3016 si.cbSize = sizeof(si);
3017 si.fMask = SIF_PAGE | SIF_RANGE;
3018 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3019 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3020 "Vertical scrollbar is invisible, should be visible.\n");
3021 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3022 "reported page/range is %d (%d..%d)\n",
3023 si.nPage, si.nMin, si.nMax);
3025 DISABLE_WS_VSCROLL(hwndRichEdit);
3026 memset(&si, 0, sizeof(si));
3027 si.cbSize = sizeof(si);
3028 si.fMask = SIF_PAGE | SIF_RANGE;
3029 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3030 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3031 "Vertical scrollbar is visible, should be invisible.\n");
3032 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3033 "reported page/range is %d (%d..%d)\n",
3034 si.nPage, si.nMin, si.nMax);
3036 /* Again, EM_SCROLL, with SB_LINEUP */
3037 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
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 DestroyWindow(hwndRichEdit);
3050 /* This window proc models what is going on with Corman Lisp 3.0.
3051 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3052 force the scrollbar into visibility. Recursion should NOT happen
3053 as a result of this action.
3055 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3056 if (r) {
3057 richeditProc = cls.lpfnWndProc;
3058 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3059 cls.lpszClassName = "RicheditStupidOverride";
3060 if(!RegisterClassA(&cls)) assert(0);
3062 recursionLevel = 0;
3063 WM_SIZE_recursionLevel = 0;
3064 bailedOutOfRecursion = FALSE;
3065 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3066 ok(!bailedOutOfRecursion,
3067 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3069 recursionLevel = 0;
3070 WM_SIZE_recursionLevel = 0;
3071 bailedOutOfRecursion = FALSE;
3072 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3073 ok(!bailedOutOfRecursion,
3074 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3076 /* Unblock window in order to process WM_DESTROY */
3077 recursionLevel = 0;
3078 bailedOutOfRecursion = FALSE;
3079 WM_SIZE_recursionLevel = 0;
3080 DestroyWindow(hwndRichEdit);
3084 static void test_EM_SETUNDOLIMIT(void)
3086 /* cases we test for:
3087 * default behaviour - limiting at 100 undo's
3088 * undo disabled - setting a limit of 0
3089 * undo limited - undo limit set to some to some number, like 2
3090 * bad input - sending a negative number should default to 100 undo's */
3092 HWND hwndRichEdit = new_richedit(NULL);
3093 CHARRANGE cr;
3094 int i;
3095 int result;
3097 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3098 cr.cpMin = 0;
3099 cr.cpMax = 1;
3100 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3101 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3102 also, multiple pastes don't combine like WM_CHAR would */
3103 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3105 /* first case - check the default */
3106 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3107 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3108 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3109 for (i=0; i<100; i++) /* Undo 100 of them */
3110 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3111 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3112 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3114 /* second case - cannot undo */
3115 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3116 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3117 SendMessage(hwndRichEdit,
3118 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3119 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3120 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3122 /* third case - set it to an arbitrary number */
3123 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3124 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3125 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3126 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3127 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3128 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3129 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3130 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3131 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3132 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3133 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3134 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3135 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3136 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3138 /* fourth case - setting negative numbers should default to 100 undos */
3139 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3140 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3141 ok (result == 100,
3142 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3144 DestroyWindow(hwndRichEdit);
3147 static void test_ES_PASSWORD(void)
3149 /* This isn't hugely testable, so we're just going to run it through its paces */
3151 HWND hwndRichEdit = new_richedit(NULL);
3152 WCHAR result;
3154 /* First, check the default of a regular control */
3155 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3156 ok (result == 0,
3157 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3159 /* Now, set it to something normal */
3160 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3161 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3162 ok (result == 120,
3163 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3165 /* Now, set it to something odd */
3166 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3167 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3168 ok (result == 1234,
3169 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3170 DestroyWindow(hwndRichEdit);
3173 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3174 LPBYTE pbBuff,
3175 LONG cb,
3176 LONG *pcb)
3178 char** str = (char**)dwCookie;
3179 *pcb = cb;
3180 if (*pcb > 0) {
3181 memcpy(*str, pbBuff, *pcb);
3182 *str += *pcb;
3184 return 0;
3187 static void test_WM_SETTEXT()
3189 HWND hwndRichEdit = new_richedit(NULL);
3190 const char * TestItem1 = "TestSomeText";
3191 const char * TestItem2 = "TestSomeText\r";
3192 const char * TestItem2_after = "TestSomeText\r\n";
3193 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3194 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3195 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3196 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3197 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3198 const char * TestItem5_after = "TestSomeText TestSomeText";
3199 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3200 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3201 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3202 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3204 char buf[1024] = {0};
3205 LRESULT result;
3206 EDITSTREAM es;
3207 char * p;
3209 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3210 any solitary \r to be converted to \r\n on return. Properly paired
3211 \r\n are not affected. It also shows that the special sequence \r\r\n
3212 gets converted to a single space.
3215 #define TEST_SETTEXT(a, b) \
3216 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3217 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3218 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3219 ok (result == lstrlen(buf), \
3220 "WM_GETTEXT returned %ld instead of expected %u\n", \
3221 result, lstrlen(buf)); \
3222 result = strcmp(b, buf); \
3223 ok(result == 0, \
3224 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3226 TEST_SETTEXT(TestItem1, TestItem1)
3227 TEST_SETTEXT(TestItem2, TestItem2_after)
3228 TEST_SETTEXT(TestItem3, TestItem3_after)
3229 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3230 TEST_SETTEXT(TestItem4, TestItem4_after)
3231 TEST_SETTEXT(TestItem5, TestItem5_after)
3232 TEST_SETTEXT(TestItem6, TestItem6_after)
3233 TEST_SETTEXT(TestItem7, TestItem7_after)
3235 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3236 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3237 p = buf;
3238 es.dwCookie = (DWORD_PTR)&p;
3239 es.dwError = 0;
3240 es.pfnCallback = test_WM_SETTEXT_esCallback;
3241 memset(buf, 0, sizeof(buf));
3242 SendMessage(hwndRichEdit, EM_STREAMOUT,
3243 (WPARAM)(SF_RTF), (LPARAM)&es);
3244 trace("EM_STREAMOUT produced: \n%s\n", buf);
3245 TEST_SETTEXT(buf, TestItem1)
3247 #undef TEST_SETTEXT
3248 DestroyWindow(hwndRichEdit);
3251 static void test_EM_STREAMOUT(void)
3253 HWND hwndRichEdit = new_richedit(NULL);
3254 int r;
3255 EDITSTREAM es;
3256 char buf[1024] = {0};
3257 char * p;
3259 const char * TestItem1 = "TestSomeText";
3260 const char * TestItem2 = "TestSomeText\r";
3261 const char * TestItem3 = "TestSomeText\r\n";
3263 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3264 p = buf;
3265 es.dwCookie = (DWORD_PTR)&p;
3266 es.dwError = 0;
3267 es.pfnCallback = test_WM_SETTEXT_esCallback;
3268 memset(buf, 0, sizeof(buf));
3269 SendMessage(hwndRichEdit, EM_STREAMOUT,
3270 (WPARAM)(SF_TEXT), (LPARAM)&es);
3271 r = strlen(buf);
3272 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3273 ok(strcmp(buf, TestItem1) == 0,
3274 "streamed text different, got %s\n", buf);
3276 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3277 p = buf;
3278 es.dwCookie = (DWORD_PTR)&p;
3279 es.dwError = 0;
3280 es.pfnCallback = test_WM_SETTEXT_esCallback;
3281 memset(buf, 0, sizeof(buf));
3282 SendMessage(hwndRichEdit, EM_STREAMOUT,
3283 (WPARAM)(SF_TEXT), (LPARAM)&es);
3284 r = strlen(buf);
3285 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3286 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3287 ok(strcmp(buf, TestItem3) == 0,
3288 "streamed text different from, got %s\n", buf);
3289 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3290 p = buf;
3291 es.dwCookie = (DWORD_PTR)&p;
3292 es.dwError = 0;
3293 es.pfnCallback = test_WM_SETTEXT_esCallback;
3294 memset(buf, 0, sizeof(buf));
3295 SendMessage(hwndRichEdit, EM_STREAMOUT,
3296 (WPARAM)(SF_TEXT), (LPARAM)&es);
3297 r = strlen(buf);
3298 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3299 ok(strcmp(buf, TestItem3) == 0,
3300 "streamed text different, got %s\n", buf);
3302 DestroyWindow(hwndRichEdit);
3305 static void test_EM_SETTEXTEX(void)
3307 HWND hwndRichEdit = new_richedit(NULL);
3308 SETTEXTEX setText;
3309 GETTEXTEX getText;
3310 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3311 'S', 'o', 'm', 'e',
3312 'T', 'e', 'x', 't', 0};
3313 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3314 't', 'S', 'o', 'm',
3315 'e', 'T', 'e', 'x',
3316 't', 't', 'S', 'o',
3317 'm', 'e', 'T', 'e',
3318 'x', 't', 0};
3319 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3320 '\r','t','S','o','m','e','T','e','x','t',0};
3321 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3322 'S', 'o', 'm', 'e',
3323 'T', 'e', 'x', 't',
3324 '\r', 0};
3325 const char * TestItem2_after = "TestSomeText\r\n";
3326 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3327 'S', 'o', 'm', 'e',
3328 'T', 'e', 'x', 't',
3329 '\r','\n','\r','\n', 0};
3330 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3331 'S', 'o', 'm', 'e',
3332 'T', 'e', 'x', 't',
3333 '\n','\n', 0};
3334 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3335 'S', 'o', 'm', 'e',
3336 'T', 'e', 'x', 't',
3337 '\r','\r', 0};
3338 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3339 'S', 'o', 'm', 'e',
3340 'T', 'e', 'x', 't',
3341 '\r','\r','\n','\r',
3342 '\n', 0};
3343 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3344 'S', 'o', 'm', 'e',
3345 'T', 'e', 'x', 't',
3346 ' ','\r', 0};
3347 #define MAX_BUF_LEN 1024
3348 WCHAR buf[MAX_BUF_LEN];
3349 char bufACP[MAX_BUF_LEN];
3350 char * p;
3351 int result;
3352 CHARRANGE cr;
3353 EDITSTREAM es;
3355 setText.codepage = 1200; /* no constant for unicode */
3356 getText.codepage = 1200; /* no constant for unicode */
3357 getText.cb = MAX_BUF_LEN;
3358 getText.flags = GT_DEFAULT;
3359 getText.lpDefaultChar = NULL;
3360 getText.lpUsedDefChar = NULL;
3362 setText.flags = 0;
3363 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3364 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3365 ok(lstrcmpW(buf, TestItem1) == 0,
3366 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3368 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3369 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3371 setText.codepage = 1200; /* no constant for unicode */
3372 getText.codepage = 1200; /* no constant for unicode */
3373 getText.cb = MAX_BUF_LEN;
3374 getText.flags = GT_DEFAULT;
3375 getText.lpDefaultChar = NULL;
3376 getText.lpUsedDefChar = NULL;
3377 setText.flags = 0;
3378 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3379 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3380 ok(lstrcmpW(buf, TestItem2) == 0,
3381 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3383 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3384 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3385 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3386 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3388 /* Baseline test for just-enough buffer space for string */
3389 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3390 getText.codepage = 1200; /* no constant for unicode */
3391 getText.flags = GT_DEFAULT;
3392 getText.lpDefaultChar = NULL;
3393 getText.lpUsedDefChar = NULL;
3394 memset(buf, 0, MAX_BUF_LEN);
3395 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3396 ok(lstrcmpW(buf, TestItem2) == 0,
3397 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3399 /* When there is enough space for one character, but not both, of the CRLF
3400 pair at the end of the string, the CR is not copied at all. That is,
3401 the caller must not see CRLF pairs truncated to CR at the end of the
3402 string.
3404 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3405 getText.codepage = 1200; /* no constant for unicode */
3406 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3407 getText.lpDefaultChar = NULL;
3408 getText.lpUsedDefChar = NULL;
3409 memset(buf, 0, MAX_BUF_LEN);
3410 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3411 ok(lstrcmpW(buf, TestItem1) == 0,
3412 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3415 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3416 setText.codepage = 1200; /* no constant for unicode */
3417 getText.codepage = 1200; /* no constant for unicode */
3418 getText.cb = MAX_BUF_LEN;
3419 getText.flags = GT_DEFAULT;
3420 getText.lpDefaultChar = NULL;
3421 getText.lpUsedDefChar = NULL;
3422 setText.flags = 0;
3423 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3424 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3425 ok(lstrcmpW(buf, TestItem3_after) == 0,
3426 "EM_SETTEXTEX did not convert properly\n");
3428 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3429 setText.codepage = 1200; /* no constant for unicode */
3430 getText.codepage = 1200; /* no constant for unicode */
3431 getText.cb = MAX_BUF_LEN;
3432 getText.flags = GT_DEFAULT;
3433 getText.lpDefaultChar = NULL;
3434 getText.lpUsedDefChar = NULL;
3435 setText.flags = 0;
3436 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3437 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3438 ok(lstrcmpW(buf, TestItem3_after) == 0,
3439 "EM_SETTEXTEX did not convert properly\n");
3441 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3442 setText.codepage = 1200; /* no constant for unicode */
3443 getText.codepage = 1200; /* no constant for unicode */
3444 getText.cb = MAX_BUF_LEN;
3445 getText.flags = GT_DEFAULT;
3446 getText.lpDefaultChar = NULL;
3447 getText.lpUsedDefChar = NULL;
3448 setText.flags = 0;
3449 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3450 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3451 ok(lstrcmpW(buf, TestItem4_after) == 0,
3452 "EM_SETTEXTEX did not convert properly\n");
3454 /* !ST_SELECTION && Unicode && !\rtf */
3455 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3456 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3458 ok (result == 1,
3459 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3460 ok(lstrlenW(buf) == 0,
3461 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3463 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3464 setText.flags = 0;
3465 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3466 /* select some text */
3467 cr.cpMax = 1;
3468 cr.cpMin = 3;
3469 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3470 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3471 setText.flags = ST_SELECTION;
3472 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3473 ok(result == 0,
3474 "EM_SETTEXTEX with NULL lParam to replace selection"
3475 " with no text should return 0. Got %i\n",
3476 result);
3478 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3479 setText.flags = 0;
3480 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3481 /* select some text */
3482 cr.cpMax = 1;
3483 cr.cpMin = 3;
3484 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3485 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3486 setText.flags = ST_SELECTION;
3487 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3488 (WPARAM)&setText, (LPARAM) TestItem1);
3489 /* get text */
3490 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3491 ok(result == lstrlenW(TestItem1),
3492 "EM_SETTEXTEX with NULL lParam to replace selection"
3493 " with no text should return 0. Got %i\n",
3494 result);
3495 ok(lstrlenW(buf) == 22,
3496 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3497 lstrlenW(buf) );
3499 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3500 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3501 p = (char *)buf;
3502 es.dwCookie = (DWORD_PTR)&p;
3503 es.dwError = 0;
3504 es.pfnCallback = test_WM_SETTEXT_esCallback;
3505 memset(buf, 0, sizeof(buf));
3506 SendMessage(hwndRichEdit, EM_STREAMOUT,
3507 (WPARAM)(SF_RTF), (LPARAM)&es);
3508 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3510 /* !ST_SELECTION && !Unicode && \rtf */
3511 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3512 getText.codepage = 1200; /* no constant for unicode */
3513 getText.cb = MAX_BUF_LEN;
3514 getText.flags = GT_DEFAULT;
3515 getText.lpDefaultChar = NULL;
3516 getText.lpUsedDefChar = NULL;
3518 setText.flags = 0;
3519 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3520 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3521 ok(lstrcmpW(buf, TestItem1) == 0,
3522 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3524 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3525 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3526 p = (char *)buf;
3527 es.dwCookie = (DWORD_PTR)&p;
3528 es.dwError = 0;
3529 es.pfnCallback = test_WM_SETTEXT_esCallback;
3530 memset(buf, 0, sizeof(buf));
3531 SendMessage(hwndRichEdit, EM_STREAMOUT,
3532 (WPARAM)(SF_RTF), (LPARAM)&es);
3533 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3535 /* select some text */
3536 cr.cpMax = 1;
3537 cr.cpMin = 3;
3538 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3540 /* ST_SELECTION && !Unicode && \rtf */
3541 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3542 getText.codepage = 1200; /* no constant for unicode */
3543 getText.cb = MAX_BUF_LEN;
3544 getText.flags = GT_DEFAULT;
3545 getText.lpDefaultChar = NULL;
3546 getText.lpUsedDefChar = NULL;
3548 setText.flags = ST_SELECTION;
3549 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3550 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3551 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3553 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3554 setText.codepage = 1200; /* no constant for unicode */
3555 getText.codepage = CP_ACP;
3556 getText.cb = MAX_BUF_LEN;
3558 setText.flags = 0;
3559 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3560 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3562 /* select some text */
3563 cr.cpMax = 1;
3564 cr.cpMin = 3;
3565 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3567 /* ST_SELECTION && !Unicode && !\rtf */
3568 setText.codepage = CP_ACP;
3569 getText.codepage = 1200; /* no constant for unicode */
3570 getText.cb = MAX_BUF_LEN;
3571 getText.flags = GT_DEFAULT;
3572 getText.lpDefaultChar = NULL;
3573 getText.lpUsedDefChar = NULL;
3575 setText.flags = ST_SELECTION;
3576 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3577 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3578 ok(lstrcmpW(buf, TestItem1alt) == 0,
3579 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3580 " using ST_SELECTION and non-Unicode\n");
3582 /* Test setting text using rich text format */
3583 setText.flags = 0;
3584 setText.codepage = CP_ACP;
3585 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3586 getText.codepage = CP_ACP;
3587 getText.cb = MAX_BUF_LEN;
3588 getText.flags = GT_DEFAULT;
3589 getText.lpDefaultChar = NULL;
3590 getText.lpUsedDefChar = NULL;
3591 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3592 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3594 setText.flags = 0;
3595 setText.codepage = CP_ACP;
3596 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3597 getText.codepage = CP_ACP;
3598 getText.cb = MAX_BUF_LEN;
3599 getText.flags = GT_DEFAULT;
3600 getText.lpDefaultChar = NULL;
3601 getText.lpUsedDefChar = NULL;
3602 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3603 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3605 DestroyWindow(hwndRichEdit);
3608 static void test_EM_LIMITTEXT(void)
3610 int ret;
3612 HWND hwndRichEdit = new_richedit(NULL);
3614 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3615 * about setting the length to -1 for multiline edit controls doesn't happen.
3618 /* Don't check default gettextlimit case. That's done in other tests */
3620 /* Set textlimit to 100 */
3621 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3622 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3623 ok (ret == 100,
3624 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3626 /* Set textlimit to 0 */
3627 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3628 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3629 ok (ret == 65536,
3630 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3632 /* Set textlimit to -1 */
3633 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3634 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3635 ok (ret == -1,
3636 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3638 /* Set textlimit to -2 */
3639 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3640 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3641 ok (ret == -2,
3642 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3644 DestroyWindow (hwndRichEdit);
3648 static void test_EM_EXLIMITTEXT(void)
3650 int i, selBegin, selEnd, len1, len2;
3651 int result;
3652 char text[1024 + 1];
3653 char buffer[1024 + 1];
3654 int textlimit = 0; /* multiple of 100 */
3655 HWND hwndRichEdit = new_richedit(NULL);
3657 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3658 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3660 textlimit = 256000;
3661 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3662 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3663 /* set higher */
3664 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3666 textlimit = 1000;
3667 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3668 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3669 /* set lower */
3670 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3672 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3673 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3674 /* default for WParam = 0 */
3675 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3677 textlimit = sizeof(text)-1;
3678 memset(text, 'W', textlimit);
3679 text[sizeof(text)-1] = 0;
3680 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3681 /* maxed out text */
3682 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3684 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3685 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3686 len1 = selEnd - selBegin;
3688 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3689 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3690 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3691 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3692 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3693 len2 = selEnd - selBegin;
3695 ok(len1 != len2,
3696 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3697 len1,len2,i);
3699 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3700 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3701 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3702 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3703 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3704 len1 = selEnd - selBegin;
3706 ok(len1 != len2,
3707 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3708 len1,len2,i);
3710 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3711 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3712 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3713 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3714 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3715 len2 = selEnd - selBegin;
3717 ok(len1 == len2,
3718 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3719 len1,len2,i);
3721 /* set text up to the limit, select all the text, then add a char */
3722 textlimit = 5;
3723 memset(text, 'W', textlimit);
3724 text[textlimit] = 0;
3725 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3726 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3727 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3728 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3729 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3730 result = strcmp(buffer, "A");
3731 ok(0 == result, "got string = \"%s\"\n", buffer);
3733 /* WM_SETTEXT not limited */
3734 textlimit = 10;
3735 memset(text, 'W', textlimit);
3736 text[textlimit] = 0;
3737 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3738 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3739 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3740 i = strlen(buffer);
3741 ok(10 == i, "expected 10 chars\n");
3742 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3743 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3745 /* try inserting more text at end */
3746 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3747 ok(0 == i, "WM_CHAR wasn't processed\n");
3748 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3749 i = strlen(buffer);
3750 ok(10 == i, "expected 10 chars, got %i\n", i);
3751 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3752 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3754 /* try inserting text at beginning */
3755 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3756 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3757 ok(0 == i, "WM_CHAR wasn't processed\n");
3758 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3759 i = strlen(buffer);
3760 ok(10 == i, "expected 10 chars, got %i\n", i);
3761 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3762 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3764 /* WM_CHAR is limited */
3765 textlimit = 1;
3766 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3767 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3768 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3769 ok(0 == i, "WM_CHAR wasn't processed\n");
3770 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3771 ok(0 == i, "WM_CHAR wasn't processed\n");
3772 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3773 i = strlen(buffer);
3774 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3776 DestroyWindow(hwndRichEdit);
3779 static void test_EM_GETLIMITTEXT(void)
3781 int i;
3782 HWND hwndRichEdit = new_richedit(NULL);
3784 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3785 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3787 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3788 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3789 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3791 DestroyWindow(hwndRichEdit);
3794 static void test_WM_SETFONT(void)
3796 /* There is no invalid input or error conditions for this function.
3797 * NULL wParam and lParam just fall back to their default values
3798 * It should be noted that even if you use a gibberish name for your fonts
3799 * here, it will still work because the name is stored. They will display as
3800 * System, but will report their name to be whatever they were created as */
3802 HWND hwndRichEdit = new_richedit(NULL);
3803 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3804 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3805 FF_DONTCARE, "Marlett");
3806 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3807 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3808 FF_DONTCARE, "MS Sans Serif");
3809 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3810 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3811 FF_DONTCARE, "Courier");
3812 LOGFONTA sentLogFont;
3813 CHARFORMAT2A returnedCF2A;
3815 returnedCF2A.cbSize = sizeof(returnedCF2A);
3817 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3818 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
3819 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3821 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3822 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3823 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3824 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3826 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
3827 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3828 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3829 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3830 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3831 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3833 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
3834 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3835 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3836 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3837 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3838 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3840 /* This last test is special since we send in NULL. We clear the variables
3841 * and just compare to "System" instead of the sent in font name. */
3842 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3843 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3844 returnedCF2A.cbSize = sizeof(returnedCF2A);
3846 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
3847 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3848 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3849 ok (!strcmp("System",returnedCF2A.szFaceName),
3850 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3852 DestroyWindow(hwndRichEdit);
3856 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3857 LPBYTE pbBuff,
3858 LONG cb,
3859 LONG *pcb)
3861 const char** str = (const char**)dwCookie;
3862 int size = strlen(*str);
3863 if(size > 3) /* let's make it piecemeal for fun */
3864 size = 3;
3865 *pcb = cb;
3866 if (*pcb > size) {
3867 *pcb = size;
3869 if (*pcb > 0) {
3870 memcpy(pbBuff, *str, *pcb);
3871 *str += *pcb;
3873 return 0;
3876 static void test_EM_GETMODIFY(void)
3878 HWND hwndRichEdit = new_richedit(NULL);
3879 LRESULT result;
3880 SETTEXTEX setText;
3881 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3882 'S', 'o', 'm', 'e',
3883 'T', 'e', 'x', 't', 0};
3884 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3885 'S', 'o', 'm', 'e',
3886 'O', 't', 'h', 'e', 'r',
3887 'T', 'e', 'x', 't', 0};
3888 const char* streamText = "hello world";
3889 CHARFORMAT2 cf2;
3890 PARAFORMAT2 pf2;
3891 EDITSTREAM es;
3893 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3894 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3895 FF_DONTCARE, "Courier");
3897 setText.codepage = 1200; /* no constant for unicode */
3898 setText.flags = ST_KEEPUNDO;
3901 /* modify flag shouldn't be set when richedit is first created */
3902 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3903 ok (result == 0,
3904 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
3906 /* setting modify flag should actually set it */
3907 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
3908 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3909 ok (result != 0,
3910 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
3912 /* clearing modify flag should actually clear it */
3913 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3914 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3915 ok (result == 0,
3916 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
3918 /* setting font doesn't change modify flag */
3919 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3920 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
3921 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3922 ok (result == 0,
3923 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
3925 /* setting text should set modify flag */
3926 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3927 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3928 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3929 ok (result != 0,
3930 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
3932 /* undo previous text doesn't reset modify flag */
3933 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3934 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3935 ok (result != 0,
3936 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
3938 /* set text with no flag to keep undo stack should not set modify flag */
3939 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3940 setText.flags = 0;
3941 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3942 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3943 ok (result == 0,
3944 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
3946 /* WM_SETTEXT doesn't modify */
3947 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3948 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3949 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3950 ok (result == 0,
3951 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3953 /* clear the text */
3954 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3955 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3956 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3957 ok (result == 0,
3958 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3960 /* replace text */
3961 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3962 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3963 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3964 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3965 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3966 ok (result != 0,
3967 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3969 /* copy/paste text 1 */
3970 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3971 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3972 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3973 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3974 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3975 ok (result != 0,
3976 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3978 /* copy/paste text 2 */
3979 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3980 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3981 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3982 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3983 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3984 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3985 ok (result != 0,
3986 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3988 /* press char */
3989 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3990 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3991 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3992 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3993 ok (result != 0,
3994 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
3996 /* press del */
3997 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3998 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3999 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4000 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4001 ok (result != 0,
4002 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4004 /* set char format */
4005 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4006 cf2.cbSize = sizeof(CHARFORMAT2);
4007 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4008 (LPARAM) &cf2);
4009 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4010 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4011 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4012 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4013 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4014 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4015 ok (result != 0,
4016 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4018 /* set para format */
4019 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4020 pf2.cbSize = sizeof(PARAFORMAT2);
4021 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4022 (LPARAM) &pf2);
4023 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4024 pf2.wAlignment = PFA_RIGHT;
4025 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4026 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4027 ok (result == 0,
4028 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4030 /* EM_STREAM */
4031 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4032 es.dwCookie = (DWORD_PTR)&streamText;
4033 es.dwError = 0;
4034 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4035 SendMessage(hwndRichEdit, EM_STREAMIN,
4036 (WPARAM)(SF_TEXT), (LPARAM)&es);
4037 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4038 ok (result != 0,
4039 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4041 DestroyWindow(hwndRichEdit);
4044 struct exsetsel_s {
4045 long min;
4046 long max;
4047 long expected_retval;
4048 int expected_getsel_start;
4049 int expected_getsel_end;
4050 int _exsetsel_todo_wine;
4051 int _getsel_todo_wine;
4054 const struct exsetsel_s exsetsel_tests[] = {
4055 /* sanity tests */
4056 {5, 10, 10, 5, 10, 0, 0},
4057 {15, 17, 17, 15, 17, 0, 0},
4058 /* test cpMax > strlen() */
4059 {0, 100, 18, 0, 18, 0, 1},
4060 /* test cpMin == cpMax */
4061 {5, 5, 5, 5, 5, 0, 0},
4062 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4063 {-1, 0, 5, 5, 5, 0, 0},
4064 {-1, 17, 5, 5, 5, 0, 0},
4065 {-1, 18, 5, 5, 5, 0, 0},
4066 /* test cpMin < 0 && cpMax < 0 */
4067 {-1, -1, 17, 17, 17, 0, 0},
4068 {-4, -5, 17, 17, 17, 0, 0},
4069 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4070 {0, -1, 18, 0, 18, 0, 1},
4071 {17, -5, 18, 17, 18, 0, 1},
4072 {18, -3, 17, 17, 17, 0, 0},
4073 /* test if cpMin > cpMax */
4074 {15, 19, 18, 15, 18, 0, 1},
4075 {19, 15, 18, 15, 18, 0, 1}
4078 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4079 CHARRANGE cr;
4080 long result;
4081 int start, end;
4083 cr.cpMin = setsel->min;
4084 cr.cpMax = setsel->max;
4085 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4087 if (setsel->_exsetsel_todo_wine) {
4088 todo_wine {
4089 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4091 } else {
4092 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4095 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4097 if (setsel->_getsel_todo_wine) {
4098 todo_wine {
4099 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);
4101 } else {
4102 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);
4106 static void test_EM_EXSETSEL(void)
4108 HWND hwndRichEdit = new_richedit(NULL);
4109 int i;
4110 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4112 /* sending some text to the window */
4113 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4114 /* 01234567890123456*/
4115 /* 10 */
4117 for (i = 0; i < num_tests; i++) {
4118 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4121 DestroyWindow(hwndRichEdit);
4124 static void test_EM_REPLACESEL(int redraw)
4126 HWND hwndRichEdit = new_richedit(NULL);
4127 char buffer[1024] = {0};
4128 int r;
4129 GETTEXTEX getText;
4130 CHARRANGE cr;
4132 /* sending some text to the window */
4133 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4134 /* 01234567890123456*/
4135 /* 10 */
4137 /* FIXME add more tests */
4138 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4139 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4140 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4141 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4142 r = strcmp(buffer, "testing");
4143 ok(0 == r, "expected %d, got %d\n", 0, r);
4145 DestroyWindow(hwndRichEdit);
4147 hwndRichEdit = new_richedit(NULL);
4149 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4150 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4152 /* Test behavior with carriage returns and newlines */
4153 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4154 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4155 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4156 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4157 r = strcmp(buffer, "RichEdit1");
4158 ok(0 == r, "expected %d, got %d\n", 0, r);
4159 getText.cb = 1024;
4160 getText.codepage = CP_ACP;
4161 getText.flags = GT_DEFAULT;
4162 getText.lpDefaultChar = NULL;
4163 getText.lpUsedDefChar = NULL;
4164 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4165 ok(strcmp(buffer, "RichEdit1") == 0,
4166 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4168 /* Test number of lines reported after EM_REPLACESEL */
4169 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4170 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4172 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4173 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4174 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4175 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4176 r = strcmp(buffer, "RichEdit1\r\n");
4177 ok(0 == r, "expected %d, got %d\n", 0, r);
4178 getText.cb = 1024;
4179 getText.codepage = CP_ACP;
4180 getText.flags = GT_DEFAULT;
4181 getText.lpDefaultChar = NULL;
4182 getText.lpUsedDefChar = NULL;
4183 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4184 ok(strcmp(buffer, "RichEdit1\r") == 0,
4185 "EM_GETTEXTEX returned incorrect string\n");
4187 /* Test number of lines reported after EM_REPLACESEL */
4188 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4189 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4191 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4192 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4193 returns the number of characters *inserted* into the control (after
4194 required conversions), but WinXP's riched20 returns the number of
4195 characters interpreted from the original lParam. Wine's builtin riched20
4196 implements the WinXP behavior.
4198 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4199 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4200 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4201 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4203 /* Test number of lines reported after EM_REPLACESEL */
4204 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4205 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4207 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4208 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4209 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4210 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4212 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4213 r = strcmp(buffer, "RichEdit1\r\n");
4214 ok(0 == r, "expected %d, got %d\n", 0, r);
4215 getText.cb = 1024;
4216 getText.codepage = CP_ACP;
4217 getText.flags = GT_DEFAULT;
4218 getText.lpDefaultChar = NULL;
4219 getText.lpUsedDefChar = NULL;
4220 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4221 ok(strcmp(buffer, "RichEdit1\r") == 0,
4222 "EM_GETTEXTEX returned incorrect string\n");
4224 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4225 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4226 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4227 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4229 /* The following tests show that richedit should handle the special \r\r\n
4230 sequence by turning it into a single space on insertion. However,
4231 EM_REPLACESEL on WinXP returns the number of characters in the original
4232 string.
4235 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4236 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4237 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4238 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4239 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4240 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4241 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4243 /* Test the actual string */
4244 getText.cb = 1024;
4245 getText.codepage = CP_ACP;
4246 getText.flags = GT_DEFAULT;
4247 getText.lpDefaultChar = NULL;
4248 getText.lpUsedDefChar = NULL;
4249 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4250 ok(strcmp(buffer, "\r\r") == 0,
4251 "EM_GETTEXTEX returned incorrect string\n");
4253 /* Test number of lines reported after EM_REPLACESEL */
4254 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4255 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4257 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4258 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4259 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4260 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4261 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4262 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4263 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4264 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4266 /* Test the actual string */
4267 getText.cb = 1024;
4268 getText.codepage = CP_ACP;
4269 getText.flags = GT_DEFAULT;
4270 getText.lpDefaultChar = NULL;
4271 getText.lpUsedDefChar = NULL;
4272 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4273 ok(strcmp(buffer, " ") == 0,
4274 "EM_GETTEXTEX returned incorrect string\n");
4276 /* Test number of lines reported after EM_REPLACESEL */
4277 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4278 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4280 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4281 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4282 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4283 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4284 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4285 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4286 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4287 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4289 /* Test the actual string */
4290 getText.cb = 1024;
4291 getText.codepage = CP_ACP;
4292 getText.flags = GT_DEFAULT;
4293 getText.lpDefaultChar = NULL;
4294 getText.lpUsedDefChar = NULL;
4295 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4296 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4297 "EM_GETTEXTEX returned incorrect string\n");
4299 /* Test number of lines reported after EM_REPLACESEL */
4300 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4301 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4303 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4304 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4305 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4306 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4307 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4308 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4309 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4310 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4312 /* Test the actual string */
4313 getText.cb = 1024;
4314 getText.codepage = CP_ACP;
4315 getText.flags = GT_DEFAULT;
4316 getText.lpDefaultChar = NULL;
4317 getText.lpUsedDefChar = NULL;
4318 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4319 ok(strcmp(buffer, " \r") == 0,
4320 "EM_GETTEXTEX returned incorrect string\n");
4322 /* Test number of lines reported after EM_REPLACESEL */
4323 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4324 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4326 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4327 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4328 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4329 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4330 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4331 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4332 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4333 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4335 /* Test the actual string */
4336 getText.cb = 1024;
4337 getText.codepage = CP_ACP;
4338 getText.flags = GT_DEFAULT;
4339 getText.lpDefaultChar = NULL;
4340 getText.lpUsedDefChar = NULL;
4341 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4342 ok(strcmp(buffer, " \r\r") == 0,
4343 "EM_GETTEXTEX returned incorrect string\n");
4345 /* Test number of lines reported after EM_REPLACESEL */
4346 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4347 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4349 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4350 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4351 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4352 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4353 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4354 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4355 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4356 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4358 /* Test the actual string */
4359 getText.cb = 1024;
4360 getText.codepage = CP_ACP;
4361 getText.flags = GT_DEFAULT;
4362 getText.lpDefaultChar = NULL;
4363 getText.lpUsedDefChar = NULL;
4364 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4365 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4366 "EM_GETTEXTEX returned incorrect string\n");
4368 /* Test number of lines reported after EM_REPLACESEL */
4369 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4370 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4372 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4373 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4374 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4375 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4376 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4377 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4378 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4380 /* Test the actual string */
4381 getText.cb = 1024;
4382 getText.codepage = CP_ACP;
4383 getText.flags = GT_DEFAULT;
4384 getText.lpDefaultChar = NULL;
4385 getText.lpUsedDefChar = NULL;
4386 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4387 ok(strcmp(buffer, "\r\r") == 0,
4388 "EM_GETTEXTEX returned incorrect string\n");
4390 /* Test number of lines reported after EM_REPLACESEL */
4391 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4392 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4394 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4395 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4396 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4397 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4398 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4399 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4400 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4401 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4403 /* Test the actual string */
4404 getText.cb = 1024;
4405 getText.codepage = CP_ACP;
4406 getText.flags = GT_DEFAULT;
4407 getText.lpDefaultChar = NULL;
4408 getText.lpUsedDefChar = NULL;
4409 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4410 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4411 "EM_GETTEXTEX returned incorrect string\n");
4413 /* Test number of lines reported after EM_REPLACESEL */
4414 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4415 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4417 if (!redraw)
4418 /* This is needed to avoid interferring with keybd_event calls
4419 * on other tests that simulate keyboard events. */
4420 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4422 DestroyWindow(hwndRichEdit);
4425 static void test_WM_PASTE(void)
4427 int result;
4428 char buffer[1024] = {0};
4429 const char* text1 = "testing paste\r";
4430 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4431 const char* text1_after = "testing paste\r\n";
4432 const char* text2 = "testing paste\r\rtesting paste";
4433 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4434 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4435 HWND hwndRichEdit = new_richedit(NULL);
4437 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4438 * to test the state of the modifiers (Ctrl/Alt/Shift).
4440 * Therefore Ctrl-<key> keystrokes need to be simulated with
4441 * keybd_event or by using SetKeyboardState to set the modifiers
4442 * and SendMessage to simulate the keystrokes.
4445 /* Sent keystrokes with keybd_event */
4446 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4447 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4448 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4449 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4450 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4452 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4453 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4455 SEND_CTRL_C(hwndRichEdit); /* Copy */
4456 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4457 SEND_CTRL_V(hwndRichEdit); /* Paste */
4458 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4459 /* Pasted text should be visible at this step */
4460 result = strcmp(text1_step1, buffer);
4461 ok(result == 0,
4462 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4464 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4465 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4466 /* Text should be the same as before (except for \r -> \r\n conversion) */
4467 result = strcmp(text1_after, buffer);
4468 ok(result == 0,
4469 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4471 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4472 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4473 SEND_CTRL_C(hwndRichEdit); /* Copy */
4474 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4475 SEND_CTRL_V(hwndRichEdit); /* Paste */
4476 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4477 /* Pasted text should be visible at this step */
4478 result = strcmp(text3, buffer);
4479 ok(result == 0,
4480 "test paste: strcmp = %i\n", result);
4481 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4482 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4483 /* Text should be the same as before (except for \r -> \r\n conversion) */
4484 result = strcmp(text2_after, buffer);
4485 ok(result == 0,
4486 "test paste: strcmp = %i\n", result);
4487 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4488 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4489 /* Text should revert to post-paste state */
4490 result = strcmp(buffer,text3);
4491 ok(result == 0,
4492 "test paste: strcmp = %i\n", result);
4494 #undef SEND_CTRL_C
4495 #undef SEND_CTRL_X
4496 #undef SEND_CTRL_V
4497 #undef SEND_CTRL_Z
4498 #undef SEND_CTRL_Y
4500 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4501 /* Send WM_CHAR to simulates Ctrl-V */
4502 SendMessage(hwndRichEdit, WM_CHAR, 22,
4503 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4504 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4505 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4506 result = strcmp(buffer,"");
4507 ok(result == 0,
4508 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4510 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4511 * with SetKeyboard state. */
4513 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4514 /* Simulates paste (Ctrl-V) */
4515 hold_key(VK_CONTROL);
4516 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4517 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4518 release_key(VK_CONTROL);
4519 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4520 result = strcmp(buffer,"paste");
4521 ok(result == 0,
4522 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4524 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4525 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4526 /* Simulates copy (Ctrl-C) */
4527 hold_key(VK_CONTROL);
4528 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4529 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4530 release_key(VK_CONTROL);
4531 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4532 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4533 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4534 result = strcmp(buffer,"testing");
4535 ok(result == 0,
4536 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4538 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4539 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4540 /* Simulates select all (Ctrl-A) */
4541 hold_key(VK_CONTROL);
4542 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4543 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4544 /* Simulates select cut (Ctrl-X) */
4545 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4546 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4547 release_key(VK_CONTROL);
4548 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4549 result = strcmp(buffer,"");
4550 ok(result == 0,
4551 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4552 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4553 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4554 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4555 result = strcmp(buffer,"cut\r\n");
4556 todo_wine ok(result == 0,
4557 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4558 /* Simulates undo (Ctrl-Z) */
4559 hold_key(VK_CONTROL);
4560 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4561 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4562 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4563 result = strcmp(buffer,"");
4564 ok(result == 0,
4565 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4566 /* Simulates redo (Ctrl-Y) */
4567 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4568 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4569 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4570 result = strcmp(buffer,"cut\r\n");
4571 todo_wine ok(result == 0,
4572 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4573 release_key(VK_CONTROL);
4575 DestroyWindow(hwndRichEdit);
4578 static void test_EM_FORMATRANGE(void)
4580 int r;
4581 FORMATRANGE fr;
4582 HDC hdc;
4583 HWND hwndRichEdit = new_richedit(NULL);
4585 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4587 hdc = GetDC(hwndRichEdit);
4588 ok(hdc != NULL, "Could not get HDC\n");
4590 fr.hdc = fr.hdcTarget = hdc;
4591 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4592 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4593 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4594 fr.chrg.cpMin = 0;
4595 fr.chrg.cpMax = 20;
4597 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4598 todo_wine {
4599 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4602 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4603 todo_wine {
4604 ok(r == 20 || r == 9, "EM_FORMATRANGE expect 20 or 9, got %d\n", r);
4607 fr.chrg.cpMin = 0;
4608 fr.chrg.cpMax = 10;
4610 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4611 todo_wine {
4612 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4615 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4616 todo_wine {
4617 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4620 DestroyWindow(hwndRichEdit);
4623 static int nCallbackCount = 0;
4625 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4626 LONG cb, LONG* pcb)
4628 const char text[] = {'t','e','s','t'};
4630 if (sizeof(text) <= cb)
4632 if ((int)dwCookie != nCallbackCount)
4634 *pcb = 0;
4635 return 0;
4638 memcpy (pbBuff, text, sizeof(text));
4639 *pcb = sizeof(text);
4641 nCallbackCount++;
4643 return 0;
4645 else
4646 return 1; /* indicates callback failed */
4649 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4650 LPBYTE pbBuff,
4651 LONG cb,
4652 LONG *pcb)
4654 const char** str = (const char**)dwCookie;
4655 int size = strlen(*str);
4656 *pcb = cb;
4657 if (*pcb > size) {
4658 *pcb = size;
4660 if (*pcb > 0) {
4661 memcpy(pbBuff, *str, *pcb);
4662 *str += *pcb;
4664 return 0;
4667 struct StringWithLength {
4668 int length;
4669 char *buffer;
4672 /* This callback is used to handled the null characters in a string. */
4673 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4674 LPBYTE pbBuff,
4675 LONG cb,
4676 LONG *pcb)
4678 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4679 int size = str->length;
4680 *pcb = cb;
4681 if (*pcb > size) {
4682 *pcb = size;
4684 if (*pcb > 0) {
4685 memcpy(pbBuff, str->buffer, *pcb);
4686 str->buffer += *pcb;
4687 str->length -= *pcb;
4689 return 0;
4692 static void test_EM_STREAMIN(void)
4694 HWND hwndRichEdit = new_richedit(NULL);
4695 LRESULT result;
4696 EDITSTREAM es;
4697 char buffer[1024] = {0};
4699 const char * streamText0 = "{\\rtf1 TestSomeText}";
4700 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4701 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4703 const char * streamText1 =
4704 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4705 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
4706 "}\r\n";
4708 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4709 const char * streamText2 =
4710 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
4711 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
4712 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
4713 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
4714 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
4715 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
4716 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4718 const char * streamText3 = "RichEdit1";
4720 struct StringWithLength cookieForStream4;
4721 const char * streamText4 =
4722 "This text just needs to be long enough to cause run to be split onto "
4723 "two separate lines and make sure the null terminating character is "
4724 "handled properly.\0";
4725 int length4 = strlen(streamText4) + 1;
4726 cookieForStream4.buffer = (char *)streamText4;
4727 cookieForStream4.length = length4;
4729 /* Minimal test without \par at the end */
4730 es.dwCookie = (DWORD_PTR)&streamText0;
4731 es.dwError = 0;
4732 es.pfnCallback = test_EM_STREAMIN_esCallback;
4733 SendMessage(hwndRichEdit, EM_STREAMIN,
4734 (WPARAM)(SF_RTF), (LPARAM)&es);
4736 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4737 ok (result == 12,
4738 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4739 result = strcmp (buffer,"TestSomeText");
4740 ok (result == 0,
4741 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4742 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4744 /* Native richedit 2.0 ignores last \par */
4745 es.dwCookie = (DWORD_PTR)&streamText0a;
4746 es.dwError = 0;
4747 es.pfnCallback = test_EM_STREAMIN_esCallback;
4748 SendMessage(hwndRichEdit, EM_STREAMIN,
4749 (WPARAM)(SF_RTF), (LPARAM)&es);
4751 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4752 ok (result == 12,
4753 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4754 result = strcmp (buffer,"TestSomeText");
4755 ok (result == 0,
4756 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4757 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4759 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4760 es.dwCookie = (DWORD_PTR)&streamText0b;
4761 es.dwError = 0;
4762 es.pfnCallback = test_EM_STREAMIN_esCallback;
4763 SendMessage(hwndRichEdit, EM_STREAMIN,
4764 (WPARAM)(SF_RTF), (LPARAM)&es);
4766 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4767 ok (result == 14,
4768 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4769 result = strcmp (buffer,"TestSomeText\r\n");
4770 ok (result == 0,
4771 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4772 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4774 es.dwCookie = (DWORD_PTR)&streamText1;
4775 es.dwError = 0;
4776 es.pfnCallback = test_EM_STREAMIN_esCallback;
4777 SendMessage(hwndRichEdit, EM_STREAMIN,
4778 (WPARAM)(SF_RTF), (LPARAM)&es);
4780 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4781 ok (result == 12,
4782 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4783 result = strcmp (buffer,"TestSomeText");
4784 ok (result == 0,
4785 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4786 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4788 es.dwCookie = (DWORD_PTR)&streamText2;
4789 es.dwError = 0;
4790 SendMessage(hwndRichEdit, EM_STREAMIN,
4791 (WPARAM)(SF_RTF), (LPARAM)&es);
4793 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4794 ok (result == 0,
4795 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4796 ok (strlen(buffer) == 0,
4797 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4798 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4800 es.dwCookie = (DWORD_PTR)&streamText3;
4801 es.dwError = 0;
4802 SendMessage(hwndRichEdit, EM_STREAMIN,
4803 (WPARAM)(SF_RTF), (LPARAM)&es);
4805 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4806 ok (result == 0,
4807 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4808 ok (strlen(buffer) == 0,
4809 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4810 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4812 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4813 es.dwError = 0;
4814 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4815 SendMessage(hwndRichEdit, EM_STREAMIN,
4816 (WPARAM)(SF_TEXT), (LPARAM)&es);
4818 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4819 ok (result == length4,
4820 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4821 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4823 DestroyWindow(hwndRichEdit);
4826 static void test_EM_StreamIn_Undo(void)
4828 /* The purpose of this test is to determine when a EM_StreamIn should be
4829 * undoable. This is important because WM_PASTE currently uses StreamIn and
4830 * pasting should always be undoable but streaming isn't always.
4832 * cases to test:
4833 * StreamIn plain text without SFF_SELECTION.
4834 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4835 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4836 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4837 * Feel free to add tests for other text modes or StreamIn things.
4841 HWND hwndRichEdit = new_richedit(NULL);
4842 LRESULT result;
4843 EDITSTREAM es;
4844 char buffer[1024] = {0};
4845 const char randomtext[] = "Some text";
4847 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4849 /* StreamIn, no SFF_SELECTION */
4850 es.dwCookie = nCallbackCount;
4851 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4852 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4853 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4854 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4855 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4856 result = strcmp (buffer,"test");
4857 ok (result == 0,
4858 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4860 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4861 ok (result == FALSE,
4862 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4864 /* StreamIn, SFF_SELECTION, but nothing selected */
4865 es.dwCookie = nCallbackCount;
4866 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4867 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4868 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4869 SendMessage(hwndRichEdit, EM_STREAMIN,
4870 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4871 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4872 result = strcmp (buffer,"testSome text");
4873 ok (result == 0,
4874 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4876 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4877 ok (result == TRUE,
4878 "EM_STREAMIN with SFF_SELECTION but no selection set "
4879 "should create an undo\n");
4881 /* StreamIn, SFF_SELECTION, with a selection */
4882 es.dwCookie = nCallbackCount;
4883 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4884 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4885 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
4886 SendMessage(hwndRichEdit, EM_STREAMIN,
4887 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4888 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4889 result = strcmp (buffer,"Sometesttext");
4890 ok (result == 0,
4891 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4893 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4894 ok (result == TRUE,
4895 "EM_STREAMIN with SFF_SELECTION and selection set "
4896 "should create an undo\n");
4898 DestroyWindow(hwndRichEdit);
4901 static BOOL is_em_settextex_supported(HWND hwnd)
4903 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
4904 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
4907 static void test_unicode_conversions(void)
4909 static const WCHAR tW[] = {'t',0};
4910 static const WCHAR teW[] = {'t','e',0};
4911 static const WCHAR textW[] = {'t','e','s','t',0};
4912 static const char textA[] = "test";
4913 char bufA[64];
4914 WCHAR bufW[64];
4915 HWND hwnd;
4916 int em_settextex_supported, ret;
4918 #define set_textA(hwnd, wm_set_text, txt) \
4919 do { \
4920 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
4921 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4922 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4923 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4924 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
4925 } while(0)
4926 #define expect_textA(hwnd, wm_get_text, txt) \
4927 do { \
4928 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4929 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4930 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4931 memset(bufA, 0xAA, sizeof(bufA)); \
4932 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4933 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4934 ret = lstrcmpA(bufA, txt); \
4935 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
4936 } while(0)
4938 #define set_textW(hwnd, wm_set_text, txt) \
4939 do { \
4940 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
4941 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4942 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4943 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4944 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
4945 } while(0)
4946 #define expect_textW(hwnd, wm_get_text, txt) \
4947 do { \
4948 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
4949 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4950 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4951 memset(bufW, 0xAA, sizeof(bufW)); \
4952 if (is_win9x) \
4954 assert(wm_get_text == EM_GETTEXTEX); \
4955 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4956 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4958 else \
4960 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4961 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
4963 ret = lstrcmpW(bufW, txt); \
4964 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
4965 } while(0)
4966 #define expect_empty(hwnd, wm_get_text) \
4967 do { \
4968 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4969 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
4970 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4971 memset(bufA, 0xAA, sizeof(bufA)); \
4972 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4973 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
4974 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
4975 } while(0)
4977 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4978 0, 0, 200, 60, 0, 0, 0, 0);
4979 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4981 ret = IsWindowUnicode(hwnd);
4982 if (is_win9x)
4983 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
4984 else
4985 ok(ret, "RichEdit20W should be unicode under NT\n");
4987 /* EM_SETTEXTEX is supported starting from version 3.0 */
4988 em_settextex_supported = is_em_settextex_supported(hwnd);
4989 trace("EM_SETTEXTEX is %ssupported on this platform\n",
4990 em_settextex_supported ? "" : "NOT ");
4992 expect_empty(hwnd, WM_GETTEXT);
4993 expect_empty(hwnd, EM_GETTEXTEX);
4995 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
4996 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
4997 expect_textA(hwnd, WM_GETTEXT, "t");
4998 expect_textA(hwnd, EM_GETTEXTEX, "t");
4999 expect_textW(hwnd, EM_GETTEXTEX, tW);
5001 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
5002 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5003 expect_textA(hwnd, WM_GETTEXT, "te");
5004 expect_textA(hwnd, EM_GETTEXTEX, "te");
5005 expect_textW(hwnd, EM_GETTEXTEX, teW);
5007 set_textA(hwnd, WM_SETTEXT, NULL);
5008 expect_empty(hwnd, WM_GETTEXT);
5009 expect_empty(hwnd, EM_GETTEXTEX);
5011 if (is_win9x)
5012 set_textA(hwnd, WM_SETTEXT, textW);
5013 else
5014 set_textA(hwnd, WM_SETTEXT, textA);
5015 expect_textA(hwnd, WM_GETTEXT, textA);
5016 expect_textA(hwnd, EM_GETTEXTEX, textA);
5017 expect_textW(hwnd, EM_GETTEXTEX, textW);
5019 if (em_settextex_supported)
5021 set_textA(hwnd, EM_SETTEXTEX, textA);
5022 expect_textA(hwnd, WM_GETTEXT, textA);
5023 expect_textA(hwnd, EM_GETTEXTEX, textA);
5024 expect_textW(hwnd, EM_GETTEXTEX, textW);
5027 if (!is_win9x)
5029 set_textW(hwnd, WM_SETTEXT, textW);
5030 expect_textW(hwnd, WM_GETTEXT, textW);
5031 expect_textA(hwnd, WM_GETTEXT, textA);
5032 expect_textW(hwnd, EM_GETTEXTEX, textW);
5033 expect_textA(hwnd, EM_GETTEXTEX, textA);
5035 if (em_settextex_supported)
5037 set_textW(hwnd, EM_SETTEXTEX, textW);
5038 expect_textW(hwnd, WM_GETTEXT, textW);
5039 expect_textA(hwnd, WM_GETTEXT, textA);
5040 expect_textW(hwnd, EM_GETTEXTEX, textW);
5041 expect_textA(hwnd, EM_GETTEXTEX, textA);
5044 DestroyWindow(hwnd);
5046 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5047 0, 0, 200, 60, 0, 0, 0, 0);
5048 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5050 ret = IsWindowUnicode(hwnd);
5051 ok(!ret, "RichEdit20A should NOT be unicode\n");
5053 set_textA(hwnd, WM_SETTEXT, textA);
5054 expect_textA(hwnd, WM_GETTEXT, textA);
5055 expect_textA(hwnd, EM_GETTEXTEX, textA);
5056 expect_textW(hwnd, EM_GETTEXTEX, textW);
5058 if (em_settextex_supported)
5060 set_textA(hwnd, EM_SETTEXTEX, textA);
5061 expect_textA(hwnd, WM_GETTEXT, textA);
5062 expect_textA(hwnd, EM_GETTEXTEX, textA);
5063 expect_textW(hwnd, EM_GETTEXTEX, textW);
5066 if (!is_win9x)
5068 set_textW(hwnd, WM_SETTEXT, textW);
5069 expect_textW(hwnd, WM_GETTEXT, textW);
5070 expect_textA(hwnd, WM_GETTEXT, textA);
5071 expect_textW(hwnd, EM_GETTEXTEX, textW);
5072 expect_textA(hwnd, EM_GETTEXTEX, textA);
5074 if (em_settextex_supported)
5076 set_textW(hwnd, EM_SETTEXTEX, textW);
5077 expect_textW(hwnd, WM_GETTEXT, textW);
5078 expect_textA(hwnd, WM_GETTEXT, textA);
5079 expect_textW(hwnd, EM_GETTEXTEX, textW);
5080 expect_textA(hwnd, EM_GETTEXTEX, textA);
5083 DestroyWindow(hwnd);
5086 static void test_WM_CHAR(void)
5088 HWND hwnd;
5089 int ret;
5090 const char * char_list = "abc\rabc\r";
5091 const char * expected_content_single = "abcabc";
5092 const char * expected_content_multi = "abc\r\nabc\r\n";
5093 char buffer[64] = {0};
5094 const char * p;
5096 /* single-line control must IGNORE carriage returns */
5097 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5098 0, 0, 200, 60, 0, 0, 0, 0);
5099 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5101 p = char_list;
5102 while (*p != '\0') {
5103 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5104 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5105 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5106 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5107 p++;
5110 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5111 ret = strcmp(buffer, expected_content_single);
5112 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5114 DestroyWindow(hwnd);
5116 /* multi-line control inserts CR normally */
5117 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5118 0, 0, 200, 60, 0, 0, 0, 0);
5119 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5121 p = char_list;
5122 while (*p != '\0') {
5123 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5124 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5125 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5126 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5127 p++;
5130 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5131 ret = strcmp(buffer, expected_content_multi);
5132 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5134 DestroyWindow(hwnd);
5137 static void test_EM_GETTEXTLENGTHEX(void)
5139 HWND hwnd;
5140 GETTEXTLENGTHEX gtl;
5141 int ret;
5142 const char * base_string = "base string";
5143 const char * test_string = "a\nb\n\n\r\n";
5144 const char * test_string_after = "a";
5145 const char * test_string_2 = "a\rtest\rstring";
5146 char buffer[64] = {0};
5148 /* single line */
5149 if (!is_win9x)
5150 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5151 0, 0, 200, 60, 0, 0, 0, 0);
5152 else
5153 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5154 0, 0, 200, 60, 0, 0, 0, 0);
5155 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5157 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5158 gtl.codepage = CP_ACP;
5159 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5160 ok(ret == 0, "ret %d\n",ret);
5162 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5163 gtl.codepage = CP_ACP;
5164 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5165 ok(ret == 0, "ret %d\n",ret);
5167 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5169 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5170 gtl.codepage = CP_ACP;
5171 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5172 ok(ret == strlen(base_string), "ret %d\n",ret);
5174 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5175 gtl.codepage = CP_ACP;
5176 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5177 ok(ret == strlen(base_string), "ret %d\n",ret);
5179 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5181 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5182 gtl.codepage = CP_ACP;
5183 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5184 ok(ret == 1, "ret %d\n",ret);
5186 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5187 gtl.codepage = CP_ACP;
5188 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5189 ok(ret == 1, "ret %d\n",ret);
5191 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5192 ret = strcmp(buffer, test_string_after);
5193 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5195 DestroyWindow(hwnd);
5197 /* multi line */
5198 if (!is_win9x)
5199 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5200 0, 0, 200, 60, 0, 0, 0, 0);
5201 else
5202 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE,
5203 0, 0, 200, 60, 0, 0, 0, 0);
5204 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5206 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5207 gtl.codepage = CP_ACP;
5208 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5209 ok(ret == 0, "ret %d\n",ret);
5211 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5212 gtl.codepage = CP_ACP;
5213 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5214 ok(ret == 0, "ret %d\n",ret);
5216 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5218 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5219 gtl.codepage = CP_ACP;
5220 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5221 ok(ret == strlen(base_string), "ret %d\n",ret);
5223 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5224 gtl.codepage = CP_ACP;
5225 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5226 ok(ret == strlen(base_string), "ret %d\n",ret);
5228 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5230 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5231 gtl.codepage = CP_ACP;
5232 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5233 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5235 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5236 gtl.codepage = CP_ACP;
5237 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5238 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5240 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5242 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5243 gtl.codepage = CP_ACP;
5244 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5245 ok(ret == 10, "ret %d\n",ret);
5247 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5248 gtl.codepage = CP_ACP;
5249 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5250 ok(ret == 6, "ret %d\n",ret);
5252 DestroyWindow(hwnd);
5256 /* globals that parent and child access when checking event masks & notifications */
5257 static HWND eventMaskEditHwnd = 0;
5258 static int queriedEventMask;
5259 static int watchForEventMask = 0;
5261 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5262 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5264 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5266 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5268 return DefWindowProcA(hwnd, message, wParam, lParam);
5271 /* test event masks in combination with WM_COMMAND */
5272 static void test_eventMask(void)
5274 HWND parent;
5275 int ret, style;
5276 WNDCLASSA cls;
5277 const char text[] = "foo bar\n";
5278 int eventMask;
5280 /* register class to capture WM_COMMAND */
5281 cls.style = 0;
5282 cls.lpfnWndProc = ParentMsgCheckProcA;
5283 cls.cbClsExtra = 0;
5284 cls.cbWndExtra = 0;
5285 cls.hInstance = GetModuleHandleA(0);
5286 cls.hIcon = 0;
5287 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5288 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5289 cls.lpszMenuName = NULL;
5290 cls.lpszClassName = "EventMaskParentClass";
5291 if(!RegisterClassA(&cls)) assert(0);
5293 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5294 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5295 ok (parent != 0, "Failed to create parent window\n");
5297 eventMaskEditHwnd = new_richedit(parent);
5298 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5300 eventMask = ENM_CHANGE | ENM_UPDATE;
5301 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5302 ok(ret == ENM_NONE, "wrong event mask\n");
5303 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5304 ok(ret == eventMask, "failed to set event mask\n");
5306 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5307 queriedEventMask = 0; /* initialize to something other than we expect */
5308 watchForEventMask = EN_CHANGE;
5309 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5310 ok(ret == TRUE, "failed to set text\n");
5311 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5312 notification in response to WM_SETTEXT */
5313 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5314 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5316 /* check to see if EN_CHANGE is sent when redraw is turned off */
5317 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5318 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5319 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5320 /* redraw is disabled by making the window invisible. */
5321 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5322 queriedEventMask = 0; /* initialize to something other than we expect */
5323 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5324 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5325 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5326 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5327 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5329 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5330 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5331 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5332 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5333 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5334 watchForEventMask = EN_UPDATE;
5335 queriedEventMask = 0; /* initialize to something other than we expect */
5336 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5337 ok(queriedEventMask == 0,
5338 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5339 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5340 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5341 queriedEventMask = 0; /* initialize to something other than we expect */
5342 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5343 ok(queriedEventMask == eventMask,
5344 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5347 DestroyWindow(parent);
5350 static int received_WM_NOTIFY = 0;
5351 static int modify_at_WM_NOTIFY = 0;
5352 static HWND hwndRichedit_WM_NOTIFY;
5354 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5356 if(message == WM_NOTIFY)
5358 received_WM_NOTIFY = 1;
5359 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5361 return DefWindowProcA(hwnd, message, wParam, lParam);
5364 static void test_WM_NOTIFY(void)
5366 HWND parent;
5367 WNDCLASSA cls;
5368 CHARFORMAT2 cf2;
5370 /* register class to capture WM_NOTIFY */
5371 cls.style = 0;
5372 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5373 cls.cbClsExtra = 0;
5374 cls.cbWndExtra = 0;
5375 cls.hInstance = GetModuleHandleA(0);
5376 cls.hIcon = 0;
5377 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5378 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5379 cls.lpszMenuName = NULL;
5380 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5381 if(!RegisterClassA(&cls)) assert(0);
5383 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5384 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5385 ok (parent != 0, "Failed to create parent window\n");
5387 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5388 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5390 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5392 /* Notifications for selection change should only be sent when selection
5393 actually changes. EM_SETCHARFORMAT is one message that calls
5394 ME_CommitUndo, which should check whether message should be sent */
5395 received_WM_NOTIFY = 0;
5396 cf2.cbSize = sizeof(CHARFORMAT2);
5397 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5398 (LPARAM) &cf2);
5399 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5400 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5401 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5402 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5404 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5405 already at 0. */
5406 received_WM_NOTIFY = 0;
5407 modify_at_WM_NOTIFY = 0;
5408 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5409 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5410 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5412 received_WM_NOTIFY = 0;
5413 modify_at_WM_NOTIFY = 0;
5414 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5415 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5417 received_WM_NOTIFY = 0;
5418 modify_at_WM_NOTIFY = 0;
5419 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5420 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5421 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5423 /* Test for WM_NOTIFY messages with redraw disabled. */
5424 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5425 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5426 received_WM_NOTIFY = 0;
5427 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5428 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5429 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5431 DestroyWindow(hwndRichedit_WM_NOTIFY);
5432 DestroyWindow(parent);
5435 static void test_undo_coalescing(void)
5437 HWND hwnd;
5438 int result;
5439 char buffer[64] = {0};
5441 /* multi-line control inserts CR normally */
5442 if (!is_win9x)
5443 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5444 0, 0, 200, 60, 0, 0, 0, 0);
5445 else
5446 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE,
5447 0, 0, 200, 60, 0, 0, 0, 0);
5448 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5450 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5451 ok (result == FALSE, "Can undo after window creation.\n");
5452 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5453 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5454 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5455 ok (result == FALSE, "Can redo after window creation.\n");
5456 result = SendMessage(hwnd, EM_REDO, 0, 0);
5457 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5459 /* Test the effect of arrows keys during typing on undo transactions*/
5460 simulate_typing_characters(hwnd, "one two three");
5461 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5462 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5463 simulate_typing_characters(hwnd, " four five six");
5465 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5466 ok (result == FALSE, "Can redo before anything is undone.\n");
5467 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5468 ok (result == TRUE, "Cannot undo typed characters.\n");
5469 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5470 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5471 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5472 ok (result == TRUE, "Cannot redo after undo.\n");
5473 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5474 result = strcmp(buffer, "one two three");
5475 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5477 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5478 ok (result == TRUE, "Cannot undo typed characters.\n");
5479 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5480 ok (result == TRUE, "Failed to undo typed characters.\n");
5481 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5482 result = strcmp(buffer, "");
5483 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5485 /* Test the effect of focus changes during typing on undo transactions*/
5486 simulate_typing_characters(hwnd, "one two three");
5487 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5488 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5489 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5490 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5491 simulate_typing_characters(hwnd, " four five six");
5492 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5493 ok (result == TRUE, "Failed to undo typed characters.\n");
5494 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5495 result = strcmp(buffer, "one two three");
5496 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5498 /* Test the effect of the back key during typing on undo transactions */
5499 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5500 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5501 ok (result == TRUE, "Failed to clear the text.\n");
5502 simulate_typing_characters(hwnd, "one two threa");
5503 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5504 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5505 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5506 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5507 simulate_typing_characters(hwnd, "e four five six");
5508 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5509 ok (result == TRUE, "Failed to undo typed characters.\n");
5510 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5511 result = strcmp(buffer, "");
5512 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5514 /* Test the effect of the delete key during typing on undo transactions */
5515 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5516 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5517 ok(result == TRUE, "Failed to set the text.\n");
5518 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5519 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5520 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5521 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5522 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5523 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5524 ok (result == TRUE, "Failed to undo typed characters.\n");
5525 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5526 result = strcmp(buffer, "acd");
5527 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5528 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5529 ok (result == TRUE, "Failed to undo typed characters.\n");
5530 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5531 result = strcmp(buffer, "abcd");
5532 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5534 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5535 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5536 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5537 ok (result == TRUE, "Failed to clear the text.\n");
5538 simulate_typing_characters(hwnd, "one two three");
5539 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5540 ok (result == 0, "expected %d but got %d\n", 0, result);
5541 simulate_typing_characters(hwnd, " four five six");
5542 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5543 ok (result == TRUE, "Failed to undo typed characters.\n");
5544 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5545 result = strcmp(buffer, "one two three");
5546 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5547 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5548 ok (result == TRUE, "Failed to undo typed characters.\n");
5549 ok (result == TRUE, "Failed to undo typed characters.\n");
5550 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5551 result = strcmp(buffer, "");
5552 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5554 DestroyWindow(hwnd);
5557 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5559 int length;
5561 /* MSDN lied, length is actually the number of bytes. */
5562 length = bytes / sizeof(WCHAR);
5563 switch(code)
5565 case WB_ISDELIMITER:
5566 return text[pos] == 'X';
5567 case WB_LEFT:
5568 case WB_MOVEWORDLEFT:
5569 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5570 return pos-1;
5571 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5572 case WB_LEFTBREAK:
5573 pos--;
5574 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5575 pos--;
5576 return pos;
5577 case WB_RIGHT:
5578 case WB_MOVEWORDRIGHT:
5579 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5580 return pos+1;
5581 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5582 case WB_RIGHTBREAK:
5583 pos++;
5584 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5585 pos++;
5586 return pos;
5587 default:
5588 ok(FALSE, "Unexpected code %d\n", code);
5589 break;
5591 return 0;
5594 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5595 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5597 static void test_word_movement(void)
5599 HWND hwnd;
5600 int result;
5601 int sel_start, sel_end;
5602 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5604 /* multi-line control inserts CR normally */
5605 hwnd = new_richedit(NULL);
5607 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5608 ok (result == TRUE, "Failed to clear the text.\n");
5609 SendMessage(hwnd, EM_SETSEL, 0, 0);
5610 /* |one two three */
5612 SEND_CTRL_RIGHT(hwnd);
5613 /* one |two three */
5614 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5615 ok(sel_start == sel_end, "Selection should be empty\n");
5616 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5618 SEND_CTRL_RIGHT(hwnd);
5619 /* one two |three */
5620 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5621 ok(sel_start == sel_end, "Selection should be empty\n");
5622 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5624 SEND_CTRL_LEFT(hwnd);
5625 /* one |two three */
5626 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5627 ok(sel_start == sel_end, "Selection should be empty\n");
5628 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5630 SEND_CTRL_LEFT(hwnd);
5631 /* |one two three */
5632 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5633 ok(sel_start == sel_end, "Selection should be empty\n");
5634 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5636 SendMessage(hwnd, EM_SETSEL, 8, 8);
5637 /* one two | three */
5638 SEND_CTRL_RIGHT(hwnd);
5639 /* one two |three */
5640 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5641 ok(sel_start == sel_end, "Selection should be empty\n");
5642 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5644 SendMessage(hwnd, EM_SETSEL, 11, 11);
5645 /* one two th|ree */
5646 SEND_CTRL_LEFT(hwnd);
5647 /* one two |three */
5648 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5649 ok(sel_start == sel_end, "Selection should be empty\n");
5650 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5652 /* Test with a custom word break procedure that uses X as the delimiter. */
5653 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5654 ok (result == TRUE, "Failed to clear the text.\n");
5655 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5656 /* |one twoXthree */
5657 SEND_CTRL_RIGHT(hwnd);
5658 /* one twoX|three */
5659 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5660 ok(sel_start == sel_end, "Selection should be empty\n");
5661 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5663 DestroyWindow(hwnd);
5665 /* Make sure the behaviour is the same with a unicode richedit window,
5666 * and using unicode functions. */
5667 if (is_win9x)
5669 skip("Cannot test with unicode richedit window\n");
5670 return;
5673 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
5674 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5675 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5677 /* Test with a custom word break procedure that uses X as the delimiter. */
5678 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
5679 ok (result == TRUE, "Failed to clear the text.\n");
5680 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5681 /* |one twoXthree */
5682 SEND_CTRL_RIGHT(hwnd);
5683 /* one twoX|three */
5684 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5685 ok(sel_start == sel_end, "Selection should be empty\n");
5686 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5688 DestroyWindow(hwnd);
5691 static void test_EM_CHARFROMPOS(void)
5693 HWND hwnd;
5694 int result;
5695 POINTL point;
5696 point.x = 0;
5697 point.y = 50;
5699 /* multi-line control inserts CR normally */
5700 hwnd = new_richedit(NULL);
5701 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5702 (LPARAM)"one two three four five six seven");
5704 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5705 ok(result == 0, "expected character index of 0 but got %d\n", result);
5707 DestroyWindow(hwnd);
5710 static void test_word_wrap(void)
5712 HWND hwnd;
5713 POINTL point = {0, 60}; /* This point must be below the first line */
5714 const char *text = "Must be long enough to test line wrapping";
5715 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
5716 int res, pos, lines;
5718 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
5719 * when specified on window creation and set later. */
5720 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5721 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5722 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5723 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5724 ok(res, "WM_SETTEXT failed.\n");
5725 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5726 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5727 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5728 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5730 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
5731 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5732 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5733 DestroyWindow(hwnd);
5735 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
5736 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5737 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5739 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5740 ok(res, "WM_SETTEXT failed.\n");
5741 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5742 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5743 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5744 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5746 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5747 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5748 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5749 DestroyWindow(hwnd);
5751 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
5752 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5753 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5754 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5755 ok(res, "WM_SETTEXT failed.\n");
5756 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5757 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5759 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5760 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5761 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5762 DestroyWindow(hwnd);
5764 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
5765 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
5766 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5767 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5768 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5769 ok(res, "WM_SETTEXT failed.\n");
5770 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5771 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5773 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5774 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5775 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5777 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
5778 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
5779 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5780 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5781 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5783 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
5784 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5785 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5786 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5787 DestroyWindow(hwnd);
5789 /* Test to see if wrapping happens with redraw disabled. */
5790 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5791 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
5792 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5793 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
5794 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
5795 ok(res, "EM_REPLACESEL failed.\n");
5796 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5797 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5798 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
5799 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5800 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5802 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5803 DestroyWindow(hwnd);
5806 static void test_auto_yscroll(void)
5808 HWND hwnd = new_richedit(NULL);
5809 int lines, ret, redraw;
5810 POINT pt;
5812 for (redraw = 0; redraw <= 1; redraw++) {
5813 trace("testing with WM_SETREDRAW=%d\n", redraw);
5814 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
5815 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
5816 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5817 ok(lines == 8, "%d lines instead of 8\n", lines);
5818 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5819 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5820 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
5821 ret = GetWindowLong(hwnd, GWL_STYLE);
5822 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
5824 SendMessage(hwnd, WM_SETTEXT, 0, 0);
5825 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5826 ok(lines == 1, "%d lines instead of 1\n", lines);
5827 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5828 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5829 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
5830 ret = GetWindowLong(hwnd, GWL_STYLE);
5831 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
5834 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5835 DestroyWindow(hwnd);
5839 static void test_format_rect(void)
5841 HWND hwnd;
5842 RECT rc, expected, clientRect;
5843 int n;
5844 DWORD options;
5846 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
5847 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5848 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5849 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
5851 GetClientRect(hwnd, &clientRect);
5853 expected = clientRect;
5854 expected.left += 1;
5855 expected.right -= 1;
5856 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5857 ok(rc.top == expected.top && rc.left == expected.left &&
5858 rc.bottom == expected.bottom && rc.right == expected.right,
5859 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5860 rc.top, rc.left, rc.bottom, rc.right,
5861 expected.top, expected.left, expected.bottom, expected.right);
5863 for (n = -3; n <= 3; n++)
5865 rc = clientRect;
5866 rc.top += n;
5867 rc.left += n;
5868 rc.bottom -= n;
5869 rc.right -= n;
5870 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
5872 expected = rc;
5873 expected.top = max(0, rc.top);
5874 expected.left = max(0, rc.left);
5875 expected.bottom = min(clientRect.bottom, rc.bottom);
5876 expected.right = min(clientRect.right, rc.right);
5877 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5878 ok(rc.top == expected.top && rc.left == expected.left &&
5879 rc.bottom == expected.bottom && rc.right == expected.right,
5880 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5881 n, rc.top, rc.left, rc.bottom, rc.right,
5882 expected.top, expected.left, expected.bottom, expected.right);
5885 rc = clientRect;
5886 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
5887 expected = clientRect;
5888 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5889 ok(rc.top == expected.top && rc.left == expected.left &&
5890 rc.bottom == expected.bottom && rc.right == expected.right,
5891 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5892 rc.top, rc.left, rc.bottom, rc.right,
5893 expected.top, expected.left, expected.bottom, expected.right);
5895 /* Adding the selectionbar adds the selectionbar width to the left side. */
5896 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
5897 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
5898 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
5899 expected.left += 8; /* selection bar width */
5900 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5901 ok(rc.top == expected.top && rc.left == expected.left &&
5902 rc.bottom == expected.bottom && rc.right == expected.right,
5903 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5904 rc.top, rc.left, rc.bottom, rc.right,
5905 expected.top, expected.left, expected.bottom, expected.right);
5907 rc = clientRect;
5908 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
5909 expected = clientRect;
5910 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5911 ok(rc.top == expected.top && rc.left == expected.left &&
5912 rc.bottom == expected.bottom && rc.right == expected.right,
5913 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5914 rc.top, rc.left, rc.bottom, rc.right,
5915 expected.top, expected.left, expected.bottom, expected.right);
5917 /* Removing the selectionbar subtracts the selectionbar width from the left side,
5918 * even if the left side is already 0. */
5919 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
5920 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
5921 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
5922 expected.left -= 8; /* selection bar width */
5923 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5924 ok(rc.top == expected.top && rc.left == expected.left &&
5925 rc.bottom == expected.bottom && rc.right == expected.right,
5926 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5927 rc.top, rc.left, rc.bottom, rc.right,
5928 expected.top, expected.left, expected.bottom, expected.right);
5930 /* Set the absolute value of the formatting rectangle. */
5931 rc = clientRect;
5932 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
5933 expected = clientRect;
5934 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5935 ok(rc.top == expected.top && rc.left == expected.left &&
5936 rc.bottom == expected.bottom && rc.right == expected.right,
5937 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5938 n, rc.top, rc.left, rc.bottom, rc.right,
5939 expected.top, expected.left, expected.bottom, expected.right);
5941 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
5942 * LPARAM as being a relative offset when the WPARAM value is 1, but these
5943 * tests show that this isn't true. */
5944 rc.top = 15;
5945 rc.left = 15;
5946 rc.bottom = clientRect.bottom - 15;
5947 rc.right = clientRect.right - 15;
5948 expected = rc;
5949 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
5950 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5951 ok(rc.top == expected.top && rc.left == expected.left &&
5952 rc.bottom == expected.bottom && rc.right == expected.right,
5953 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5954 rc.top, rc.left, rc.bottom, rc.right,
5955 expected.top, expected.left, expected.bottom, expected.right);
5957 /* For some reason it does not limit the values to the client rect with
5958 * a WPARAM value of 1. */
5959 rc.top = -15;
5960 rc.left = -15;
5961 rc.bottom = clientRect.bottom + 15;
5962 rc.right = clientRect.right + 15;
5963 expected = rc;
5964 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
5965 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5966 ok(rc.top == expected.top && rc.left == expected.left &&
5967 rc.bottom == expected.bottom && rc.right == expected.right,
5968 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5969 rc.top, rc.left, rc.bottom, rc.right,
5970 expected.top, expected.left, expected.bottom, expected.right);
5972 DestroyWindow(hwnd);
5974 /* The extended window style affects the formatting rectangle. */
5975 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
5976 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5977 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5978 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
5980 GetClientRect(hwnd, &clientRect);
5982 expected = clientRect;
5983 expected.left += 1;
5984 expected.top += 1;
5985 expected.right -= 1;
5986 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5987 ok(rc.top == expected.top && rc.left == expected.left &&
5988 rc.bottom == expected.bottom && rc.right == expected.right,
5989 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5990 rc.top, rc.left, rc.bottom, rc.right,
5991 expected.top, expected.left, expected.bottom, expected.right);
5993 rc = clientRect;
5994 rc.top += 5;
5995 rc.left += 5;
5996 rc.bottom -= 5;
5997 rc.right -= 5;
5998 expected = rc;
5999 expected.top -= 1;
6000 expected.left -= 1;
6001 expected.right += 1;
6002 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6003 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6004 ok(rc.top == expected.top && rc.left == expected.left &&
6005 rc.bottom == expected.bottom && rc.right == expected.right,
6006 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6007 rc.top, rc.left, rc.bottom, rc.right,
6008 expected.top, expected.left, expected.bottom, expected.right);
6010 DestroyWindow(hwnd);
6013 START_TEST( editor )
6015 /* Must explicitly LoadLibrary(). The test has no references to functions in
6016 * RICHED20.DLL, so the linker doesn't actually link to it. */
6017 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
6018 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
6020 is_win9x = GetVersion() & 0x80000000;
6022 test_WM_CHAR();
6023 test_EM_FINDTEXT();
6024 test_EM_GETLINE();
6025 test_EM_POSFROMCHAR();
6026 test_EM_SCROLLCARET();
6027 test_EM_SCROLL();
6028 test_scrollbar_visibility();
6029 test_WM_SETTEXT();
6030 test_EM_LINELENGTH();
6031 test_EM_SETCHARFORMAT();
6032 test_EM_SETTEXTMODE();
6033 test_TM_PLAINTEXT();
6034 test_EM_SETOPTIONS();
6035 test_WM_GETTEXT();
6036 test_EM_GETTEXTRANGE();
6037 test_EM_GETSELTEXT();
6038 test_EM_SETUNDOLIMIT();
6039 test_ES_PASSWORD();
6040 test_EM_SETTEXTEX();
6041 test_EM_LIMITTEXT();
6042 test_EM_EXLIMITTEXT();
6043 test_EM_GETLIMITTEXT();
6044 test_WM_SETFONT();
6045 test_EM_GETMODIFY();
6046 test_EM_EXSETSEL();
6047 test_WM_PASTE();
6048 test_EM_STREAMIN();
6049 test_EM_STREAMOUT();
6050 test_EM_StreamIn_Undo();
6051 test_EM_FORMATRANGE();
6052 test_unicode_conversions();
6053 test_EM_GETTEXTLENGTHEX();
6054 test_EM_REPLACESEL(1);
6055 test_EM_REPLACESEL(0);
6056 test_WM_NOTIFY();
6057 test_EM_AUTOURLDETECT();
6058 test_eventMask();
6059 test_undo_coalescing();
6060 test_word_movement();
6061 test_EM_CHARFROMPOS();
6062 test_SETPARAFORMAT();
6063 test_word_wrap();
6064 test_auto_yscroll();
6065 test_format_rect();
6067 /* Set the environment variable WINETEST_RICHED20 to keep windows
6068 * responsive and open for 30 seconds. This is useful for debugging.
6070 if (getenv( "WINETEST_RICHED20" )) {
6071 keep_responsive(30);
6074 OleFlushClipboard();
6075 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());