riched20/tests: Fixed typo in test_EM_STREAMIN.
[wine.git] / dlls / riched20 / tests / editor.c
blob27f4feed3d7b9e6bf23168543985792b9ae3ac00
1 /*
2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
36 #define ID_RICHEDITTESTDBUTTON 0x123
38 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
40 #define ok_w3(format, szString1, szString2, szString3) \
41 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
42 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
43 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
44 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
45 format, string1, string2, string3);
47 static HMODULE hmoduleRichEdit;
49 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
50 HWND hwnd;
51 hwnd = CreateWindowA(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_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
59 HWND hwnd;
60 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
61 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
62 hmoduleRichEdit, NULL);
63 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
64 return hwnd;
67 static HWND new_richedit(HWND parent) {
68 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
71 static HWND new_richeditW(HWND parent) {
72 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
75 /* Keeps the window reponsive for the deley_time in seconds.
76 * This is useful for debugging a test to see what is happening. */
77 static void keep_responsive(time_t delay_time)
79 MSG msg;
80 time_t end;
82 /* The message pump uses PeekMessage() to empty the queue and then
83 * sleeps for 50ms before retrying the queue. */
84 end = time(NULL) + delay_time;
85 while (time(NULL) < end) {
86 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
87 TranslateMessage(&msg);
88 DispatchMessage(&msg);
89 } else {
90 Sleep(50);
95 static void simulate_typing_characters(HWND hwnd, const char* szChars)
97 int ret;
99 while (*szChars != '\0') {
100 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
101 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
102 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
103 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
104 szChars++;
108 static BOOL hold_key(int vk)
110 BYTE key_state[256];
111 BOOL result;
113 result = GetKeyboardState(key_state);
114 ok(result, "GetKeyboardState failed.\n");
115 if (!result) return FALSE;
116 key_state[vk] |= 0x80;
117 result = SetKeyboardState(key_state);
118 ok(result, "SetKeyboardState failed.\n");
119 return result != 0;
122 static BOOL release_key(int vk)
124 BYTE key_state[256];
125 BOOL result;
127 result = GetKeyboardState(key_state);
128 ok(result, "GetKeyboardState failed.\n");
129 if (!result) return FALSE;
130 key_state[vk] &= ~0x80;
131 result = SetKeyboardState(key_state);
132 ok(result, "SetKeyboardState failed.\n");
133 return result != 0;
136 static const char haystack[] = "WINEWine wineWine wine WineWine";
137 /* ^0 ^10 ^20 ^30 */
139 struct find_s {
140 int start;
141 int end;
142 const char *needle;
143 int flags;
144 int expected_loc;
148 static struct find_s find_tests[] = {
149 /* Find in empty text */
150 {0, -1, "foo", FR_DOWN, -1},
151 {0, -1, "foo", 0, -1},
152 {0, -1, "", FR_DOWN, -1},
153 {20, 5, "foo", FR_DOWN, -1},
154 {5, 20, "foo", FR_DOWN, -1}
157 static struct find_s find_tests2[] = {
158 /* No-result find */
159 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
160 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
162 /* Subsequent finds */
163 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
164 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
165 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
166 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
168 /* Find backwards */
169 {19, 20, "Wine", FR_MATCHCASE, 13},
170 {10, 20, "Wine", FR_MATCHCASE, 4},
171 {20, 10, "Wine", FR_MATCHCASE, 13},
173 /* Case-insensitive */
174 {1, 31, "wInE", FR_DOWN, 4},
175 {1, 31, "Wine", FR_DOWN, 4},
177 /* High-to-low ranges */
178 {20, 5, "Wine", FR_DOWN, -1},
179 {2, 1, "Wine", FR_DOWN, -1},
180 {30, 29, "Wine", FR_DOWN, -1},
181 {20, 5, "Wine", 0, 13},
183 /* Find nothing */
184 {5, 10, "", FR_DOWN, -1},
185 {10, 5, "", FR_DOWN, -1},
186 {0, -1, "", FR_DOWN, -1},
187 {10, 5, "", 0, -1},
189 /* Whole-word search */
190 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
191 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
192 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
193 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
194 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
195 {11, -1, "winewine", FR_WHOLEWORD, 0},
196 {31, -1, "winewine", FR_WHOLEWORD, 23},
198 /* Bad ranges */
199 {5, 200, "XXX", FR_DOWN, -1},
200 {-20, 20, "Wine", FR_DOWN, -1},
201 {-20, 20, "Wine", FR_DOWN, -1},
202 {-15, -20, "Wine", FR_DOWN, -1},
203 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
205 /* Check the case noted in bug 4479 where matches at end aren't recognized */
206 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
207 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
208 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
209 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
210 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
212 /* The backwards case of bug 4479; bounds look right
213 * Fails because backward find is wrong */
214 {19, 20, "WINE", FR_MATCHCASE, 0},
215 {0, 20, "WINE", FR_MATCHCASE, -1},
217 {0, -1, "wineWine wine", 0, -1},
220 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
221 int findloc;
222 FINDTEXT ft;
223 memset(&ft, 0, sizeof(ft));
224 ft.chrg.cpMin = f->start;
225 ft.chrg.cpMax = f->end;
226 ft.lpstrText = f->needle;
227 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
228 ok(findloc == f->expected_loc,
229 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
230 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
233 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
234 int id) {
235 int findloc;
236 FINDTEXTEX ft;
237 int expected_end_loc;
239 memset(&ft, 0, sizeof(ft));
240 ft.chrg.cpMin = f->start;
241 ft.chrg.cpMax = f->end;
242 ft.lpstrText = f->needle;
243 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
244 ok(findloc == f->expected_loc,
245 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
246 name, id, f->needle, f->start, f->end, f->flags, findloc);
247 ok(ft.chrgText.cpMin == f->expected_loc,
248 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
249 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
250 expected_end_loc = ((f->expected_loc == -1) ? -1
251 : f->expected_loc + strlen(f->needle));
252 ok(ft.chrgText.cpMax == expected_end_loc,
253 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
254 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
257 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
258 int num_tests)
260 int i;
262 for (i = 0; i < num_tests; i++) {
263 check_EM_FINDTEXT(hwnd, name, &find[i], i);
264 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
268 static void test_EM_FINDTEXT(void)
270 HWND hwndRichEdit = new_richedit(NULL);
271 CHARFORMAT2 cf2;
273 /* Empty rich edit control */
274 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
275 sizeof(find_tests)/sizeof(struct find_s));
277 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
279 /* Haystack text */
280 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
281 sizeof(find_tests2)/sizeof(struct find_s));
283 /* Setting a format on an arbitrary range should have no effect in search
284 results. This tests correct offset reporting across runs. */
285 cf2.cbSize = sizeof(CHARFORMAT2);
286 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
287 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
288 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
289 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
290 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
292 /* Haystack text, again */
293 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
294 sizeof(find_tests2)/sizeof(struct find_s));
296 /* Yet another range */
297 cf2.dwMask = CFM_BOLD | cf2.dwMask;
298 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
299 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
300 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
302 /* Haystack text, again */
303 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
304 sizeof(find_tests2)/sizeof(struct find_s));
306 DestroyWindow(hwndRichEdit);
309 static const struct getline_s {
310 int line;
311 size_t buffer_len;
312 const char *text;
313 } gl[] = {
314 {0, 10, "foo bar\r"},
315 {1, 10, "\r"},
316 {2, 10, "bar\r"},
317 {3, 10, "\r"},
319 /* Buffer smaller than line length */
320 {0, 2, "foo bar\r"},
321 {0, 1, "foo bar\r"},
322 {0, 0, "foo bar\r"}
325 static void test_EM_GETLINE(void)
327 int i;
328 HWND hwndRichEdit = new_richedit(NULL);
329 static const int nBuf = 1024;
330 char dest[1024], origdest[1024];
331 const char text[] = "foo bar\n"
332 "\n"
333 "bar\n";
335 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
337 memset(origdest, 0xBB, nBuf);
338 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
340 int nCopied;
341 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
342 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
343 memset(dest, 0xBB, nBuf);
344 *(WORD *) dest = gl[i].buffer_len;
346 /* EM_GETLINE appends a "\r\0" to the end of the line
347 * nCopied counts up to and including the '\r' */
348 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
349 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
350 expected_nCopied);
351 /* two special cases since a parameter is passed via dest */
352 if (gl[i].buffer_len == 0)
353 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
354 "buffer_len=0\n");
355 else if (gl[i].buffer_len == 1)
356 ok(dest[0] == gl[i].text[0] && !dest[1] &&
357 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
358 else
360 /* Prepare hex strings of buffers to dump on failure. */
361 char expectedbuf[1024];
362 char resultbuf[1024];
363 int j;
364 resultbuf[0] = '\0';
365 for (j = 0; j < 32; j++)
366 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
367 expectedbuf[0] = '\0';
368 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
369 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
370 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
371 sprintf(expectedbuf+strlen(expectedbuf), "??");
372 for (; j < 32; j++) /* Bytes after declared buffer size */
373 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
375 /* Test the part of the buffer that is expected to be written according
376 * to the MSDN documentation fo EM_GETLINE, which does not state that
377 * a NULL terminating character will be added unless no text is copied.
379 * Windows NT does not append a NULL terminating character, but
380 * Windows 2000 and up do append a NULL terminating character if there
381 * is space in the buffer. The test will ignore this difference. */
382 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
383 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
384 i, expected_bytes_written, expectedbuf, resultbuf);
385 /* Test the part of the buffer after the declared length to make sure
386 * there are no buffer overruns. */
387 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
388 nBuf - gl[i].buffer_len),
389 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
390 i, expected_bytes_written, expectedbuf, resultbuf);
394 DestroyWindow(hwndRichEdit);
397 static void test_EM_LINELENGTH(void)
399 HWND hwndRichEdit = new_richedit(NULL);
400 const char * text =
401 "richedit1\r"
402 "richedit1\n"
403 "richedit1\r\n"
404 "richedit1";
405 int offset_test[10][2] = {
406 {0, 9},
407 {5, 9},
408 {10, 9},
409 {15, 9},
410 {20, 9},
411 {25, 9},
412 {30, 9},
413 {35, 9},
414 {40, 0},
415 {45, 0},
417 int i;
418 LRESULT result;
420 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
422 for (i = 0; i < 10; i++) {
423 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
424 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
425 offset_test[i][0], result, offset_test[i][1]);
428 DestroyWindow(hwndRichEdit);
431 static int get_scroll_pos_y(HWND hwnd)
433 POINT p = {-1, -1};
434 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
435 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
436 return p.y;
439 static void move_cursor(HWND hwnd, LONG charindex)
441 CHARRANGE cr;
442 cr.cpMax = charindex;
443 cr.cpMin = charindex;
444 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
447 static void line_scroll(HWND hwnd, int amount)
449 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
452 static void test_EM_SCROLLCARET(void)
454 int prevY, curY;
455 const char text[] = "aa\n"
456 "this is a long line of text that should be longer than the "
457 "control's width\n"
458 "cc\n"
459 "dd\n"
460 "ee\n"
461 "ff\n"
462 "gg\n"
463 "hh\n";
464 /* The richedit window height needs to be large enough vertically to fit in
465 * more than two lines of text, so the new_richedit function can't be used
466 * since a height of 60 was not large enough on some systems.
468 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
469 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
470 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
471 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
473 /* Can't verify this */
474 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
476 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
478 /* Caret above visible window */
479 line_scroll(hwndRichEdit, 3);
480 prevY = get_scroll_pos_y(hwndRichEdit);
481 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
482 curY = get_scroll_pos_y(hwndRichEdit);
483 ok(prevY != curY, "%d == %d\n", prevY, curY);
485 /* Caret below visible window */
486 move_cursor(hwndRichEdit, sizeof(text) - 1);
487 line_scroll(hwndRichEdit, -3);
488 prevY = get_scroll_pos_y(hwndRichEdit);
489 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
490 curY = get_scroll_pos_y(hwndRichEdit);
491 ok(prevY != curY, "%d == %d\n", prevY, curY);
493 /* Caret in visible window */
494 move_cursor(hwndRichEdit, sizeof(text) - 2);
495 prevY = get_scroll_pos_y(hwndRichEdit);
496 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
497 curY = get_scroll_pos_y(hwndRichEdit);
498 ok(prevY == curY, "%d != %d\n", prevY, curY);
500 /* Caret still in visible window */
501 line_scroll(hwndRichEdit, -1);
502 prevY = get_scroll_pos_y(hwndRichEdit);
503 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
504 curY = get_scroll_pos_y(hwndRichEdit);
505 ok(prevY == curY, "%d != %d\n", prevY, curY);
507 DestroyWindow(hwndRichEdit);
510 static void test_EM_POSFROMCHAR(void)
512 HWND hwndRichEdit = new_richedit(NULL);
513 int i, expected;
514 LRESULT result;
515 unsigned int height = 0;
516 int xpos = 0;
517 POINTL pt;
518 LOCALESIGNATURE sig;
519 BOOL rtl;
520 static const char text[] = "aa\n"
521 "this is a long line of text that should be longer than the "
522 "control's width\n"
523 "cc\n"
524 "dd\n"
525 "ee\n"
526 "ff\n"
527 "gg\n"
528 "hh\n";
530 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
531 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
532 (sig.lsUsb[3] & 0x08000000) != 0);
534 /* Fill the control to lines to ensure that most of them are offscreen */
535 for (i = 0; i < 50; i++)
537 /* Do not modify the string; it is exactly 16 characters long. */
538 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
539 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
543 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
544 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
545 Richedit 3.0 accepts either of the above API conventions.
548 /* Testing Richedit 2.0 API format */
550 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
551 Since all lines are identical and drawn with the same font,
552 they should have the same height... right?
554 for (i = 0; i < 50; i++)
556 /* All the lines are 16 characters long */
557 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
558 if (i == 0)
560 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
561 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
562 xpos = LOWORD(result);
564 else if (i == 1)
566 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
567 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
568 height = HIWORD(result);
570 else
572 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
573 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
577 /* Testing position at end of text */
578 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
579 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
580 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
582 /* Testing position way past end of text */
583 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
584 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
585 expected = (rtl ? 8 : 1);
586 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
588 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
589 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
590 for (i = 0; i < 50; i++)
592 /* All the lines are 16 characters long */
593 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
594 ok((signed short)(HIWORD(result)) == (i - 1) * height,
595 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
596 (signed short)(HIWORD(result)), (i - 1) * height);
597 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
600 /* Testing position at end of text */
601 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
602 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
603 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
605 /* Testing position way past end of text */
606 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
607 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
608 expected = (rtl ? 8 : 1);
609 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
611 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
612 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
613 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
615 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
616 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
617 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
618 xpos = LOWORD(result);
620 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
621 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
622 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
623 ok((signed short)(LOWORD(result)) < xpos,
624 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
625 (signed short)(LOWORD(result)), xpos);
626 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
628 /* Test around end of text that doesn't end in a newline. */
629 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
630 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
631 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
632 ok(pt.x > 1, "pt.x = %d\n", pt.x);
633 xpos = pt.x;
634 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
635 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
636 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
637 xpos = (rtl ? pt.x + 7 : pt.x);
638 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
639 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
640 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
642 /* Try a negative position. */
643 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
644 ok(pt.x == 1, "pt.x = %d\n", pt.x);
646 DestroyWindow(hwndRichEdit);
649 static void test_EM_SETCHARFORMAT(void)
651 HWND hwndRichEdit = new_richedit(NULL);
652 CHARFORMAT2 cf2;
653 int rc = 0;
654 int tested_effects[] = {
655 CFE_BOLD,
656 CFE_ITALIC,
657 CFE_UNDERLINE,
658 CFE_STRIKEOUT,
659 CFE_PROTECTED,
660 CFE_LINK,
661 CFE_SUBSCRIPT,
662 CFE_SUPERSCRIPT,
665 int i;
666 CHARRANGE cr;
667 LOCALESIGNATURE sig;
668 BOOL rtl;
670 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
671 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
672 (sig.lsUsb[3] & 0x08000000) != 0);
674 /* Invalid flags, CHARFORMAT2 structure blanked out */
675 memset(&cf2, 0, sizeof(cf2));
676 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
677 (LPARAM) &cf2);
678 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
680 /* A valid flag, CHARFORMAT2 structure blanked out */
681 memset(&cf2, 0, sizeof(cf2));
682 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
683 (LPARAM) &cf2);
684 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
686 /* A valid flag, CHARFORMAT2 structure blanked out */
687 memset(&cf2, 0, sizeof(cf2));
688 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
689 (LPARAM) &cf2);
690 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
692 /* A valid flag, CHARFORMAT2 structure blanked out */
693 memset(&cf2, 0, sizeof(cf2));
694 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
695 (LPARAM) &cf2);
696 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
698 /* A valid flag, CHARFORMAT2 structure blanked out */
699 memset(&cf2, 0, sizeof(cf2));
700 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
701 (LPARAM) &cf2);
702 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
704 /* Invalid flags, CHARFORMAT2 structure minimally filled */
705 memset(&cf2, 0, sizeof(cf2));
706 cf2.cbSize = sizeof(CHARFORMAT2);
707 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
708 (LPARAM) &cf2);
709 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
710 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
711 ok(rc == FALSE, "Should not be able to undo here.\n");
712 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
714 /* A valid flag, CHARFORMAT2 structure minimally filled */
715 memset(&cf2, 0, sizeof(cf2));
716 cf2.cbSize = sizeof(CHARFORMAT2);
717 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
718 (LPARAM) &cf2);
719 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
720 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
721 ok(rc == FALSE, "Should not be able to undo here.\n");
722 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
724 /* A valid flag, CHARFORMAT2 structure minimally filled */
725 memset(&cf2, 0, sizeof(cf2));
726 cf2.cbSize = sizeof(CHARFORMAT2);
727 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
728 (LPARAM) &cf2);
729 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
730 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
731 ok(rc == FALSE, "Should not be able to undo here.\n");
732 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
734 /* A valid flag, CHARFORMAT2 structure minimally filled */
735 memset(&cf2, 0, sizeof(cf2));
736 cf2.cbSize = sizeof(CHARFORMAT2);
737 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
738 (LPARAM) &cf2);
739 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
740 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
741 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
742 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
744 /* A valid flag, CHARFORMAT2 structure minimally filled */
745 memset(&cf2, 0, sizeof(cf2));
746 cf2.cbSize = sizeof(CHARFORMAT2);
747 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
748 (LPARAM) &cf2);
749 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
750 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
751 ok(rc == TRUE, "Should not be able to undo here.\n");
752 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
754 cf2.cbSize = sizeof(CHARFORMAT2);
755 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
756 (LPARAM) &cf2);
758 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
759 cf2.cbSize = sizeof(CHARFORMAT2);
760 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
761 (LPARAM) &cf2);
762 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
763 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
765 /* wParam==0 is default char format, does not set modify */
766 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
767 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
768 ok(rc == 0, "Text marked as modified, expected not modified!\n");
769 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
770 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
771 if (! rtl)
773 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
774 ok(rc == 0, "Text marked as modified, expected not modified!\n");
776 else
777 skip("RTL language found\n");
779 /* wParam==SCF_SELECTION sets modify if nonempty selection */
780 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
781 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
782 ok(rc == 0, "Text marked as modified, expected not modified!\n");
783 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
784 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
785 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
786 ok(rc == 0, "Text marked as modified, expected not modified!\n");
788 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
789 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
790 ok(rc == 0, "Text marked as modified, expected not modified!\n");
791 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
792 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
793 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
794 ok(rc == 0, "Text marked as modified, expected not modified!\n");
795 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
796 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
797 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
798 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
799 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
801 /* wParam==SCF_ALL sets modify regardless of whether text is present */
802 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
803 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
804 ok(rc == 0, "Text marked as modified, expected not modified!\n");
805 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
806 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
807 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
808 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
810 DestroyWindow(hwndRichEdit);
812 /* EM_GETCHARFORMAT tests */
813 for (i = 0; tested_effects[i]; i++)
815 hwndRichEdit = new_richedit(NULL);
816 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
818 /* Need to set a TrueType font to get consistent CFM_BOLD results */
819 memset(&cf2, 0, sizeof(CHARFORMAT2));
820 cf2.cbSize = sizeof(CHARFORMAT2);
821 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
822 cf2.dwEffects = 0;
823 strcpy(cf2.szFaceName, "Courier New");
824 cf2.wWeight = FW_DONTCARE;
825 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
827 memset(&cf2, 0, sizeof(CHARFORMAT2));
828 cf2.cbSize = sizeof(CHARFORMAT2);
829 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
830 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
831 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
832 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
834 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
835 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
836 ok((cf2.dwEffects & tested_effects[i]) == 0,
837 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
839 memset(&cf2, 0, sizeof(CHARFORMAT2));
840 cf2.cbSize = sizeof(CHARFORMAT2);
841 cf2.dwMask = tested_effects[i];
842 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
843 cf2.dwMask = CFM_SUPERSCRIPT;
844 cf2.dwEffects = tested_effects[i];
845 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
846 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
848 memset(&cf2, 0, sizeof(CHARFORMAT2));
849 cf2.cbSize = sizeof(CHARFORMAT2);
850 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
851 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
852 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
853 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
855 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
856 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
857 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
858 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
860 memset(&cf2, 0, sizeof(CHARFORMAT2));
861 cf2.cbSize = sizeof(CHARFORMAT2);
862 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
863 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
864 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
865 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
867 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
868 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
869 ok((cf2.dwEffects & tested_effects[i]) == 0,
870 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
872 memset(&cf2, 0, sizeof(CHARFORMAT2));
873 cf2.cbSize = sizeof(CHARFORMAT2);
874 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
875 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
876 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
877 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
879 (cf2.dwMask & tested_effects[i]) == 0),
880 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
882 DestroyWindow(hwndRichEdit);
885 for (i = 0; tested_effects[i]; i++)
887 hwndRichEdit = new_richedit(NULL);
888 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
890 /* Need to set a TrueType font to get consistent CFM_BOLD results */
891 memset(&cf2, 0, sizeof(CHARFORMAT2));
892 cf2.cbSize = sizeof(CHARFORMAT2);
893 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
894 cf2.dwEffects = 0;
895 strcpy(cf2.szFaceName, "Courier New");
896 cf2.wWeight = FW_DONTCARE;
897 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
899 memset(&cf2, 0, sizeof(CHARFORMAT2));
900 cf2.cbSize = sizeof(CHARFORMAT2);
901 cf2.dwMask = tested_effects[i];
902 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
903 cf2.dwMask = CFM_SUPERSCRIPT;
904 cf2.dwEffects = tested_effects[i];
905 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
906 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
908 memset(&cf2, 0, sizeof(CHARFORMAT2));
909 cf2.cbSize = sizeof(CHARFORMAT2);
910 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
911 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
912 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
913 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
915 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
916 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
917 ok((cf2.dwEffects & tested_effects[i]) == 0,
918 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
920 memset(&cf2, 0, sizeof(CHARFORMAT2));
921 cf2.cbSize = sizeof(CHARFORMAT2);
922 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
923 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
924 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
925 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
927 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
928 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
929 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
930 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
932 memset(&cf2, 0, sizeof(CHARFORMAT2));
933 cf2.cbSize = sizeof(CHARFORMAT2);
934 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
935 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
936 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
937 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
939 (cf2.dwMask & tested_effects[i]) == 0),
940 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
941 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
942 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
944 DestroyWindow(hwndRichEdit);
947 /* Effects applied on an empty selection should take effect when selection is
948 replaced with text */
949 hwndRichEdit = new_richedit(NULL);
950 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
951 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
953 memset(&cf2, 0, sizeof(CHARFORMAT2));
954 cf2.cbSize = sizeof(CHARFORMAT2);
955 cf2.dwMask = CFM_BOLD;
956 cf2.dwEffects = CFE_BOLD;
957 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
959 /* Selection is now nonempty */
960 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
962 memset(&cf2, 0, sizeof(CHARFORMAT2));
963 cf2.cbSize = sizeof(CHARFORMAT2);
964 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
965 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
967 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
968 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
969 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
970 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
973 /* Set two effects on an empty selection */
974 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
975 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
977 memset(&cf2, 0, sizeof(CHARFORMAT2));
978 cf2.cbSize = sizeof(CHARFORMAT2);
979 cf2.dwMask = CFM_BOLD;
980 cf2.dwEffects = CFE_BOLD;
981 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
982 cf2.dwMask = CFM_ITALIC;
983 cf2.dwEffects = CFE_ITALIC;
984 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
986 /* Selection is now nonempty */
987 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
989 memset(&cf2, 0, sizeof(CHARFORMAT2));
990 cf2.cbSize = sizeof(CHARFORMAT2);
991 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
992 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
994 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
995 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
996 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
997 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
999 /* Setting the (empty) selection to exactly the same place as before should
1000 NOT clear the insertion style! */
1001 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1002 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1004 memset(&cf2, 0, sizeof(CHARFORMAT2));
1005 cf2.cbSize = sizeof(CHARFORMAT2);
1006 cf2.dwMask = CFM_BOLD;
1007 cf2.dwEffects = CFE_BOLD;
1008 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1010 /* Empty selection in same place, insert style should NOT be forgotten here. */
1011 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1013 /* Selection is now nonempty */
1014 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1016 memset(&cf2, 0, sizeof(CHARFORMAT2));
1017 cf2.cbSize = sizeof(CHARFORMAT2);
1018 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1019 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1021 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1022 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1023 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1024 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1026 /* Ditto with EM_EXSETSEL */
1027 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1028 cr.cpMin = 2; cr.cpMax = 2;
1029 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1031 memset(&cf2, 0, sizeof(CHARFORMAT2));
1032 cf2.cbSize = sizeof(CHARFORMAT2);
1033 cf2.dwMask = CFM_BOLD;
1034 cf2.dwEffects = CFE_BOLD;
1035 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1037 /* Empty selection in same place, insert style should NOT be forgotten here. */
1038 cr.cpMin = 2; cr.cpMax = 2;
1039 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1041 /* Selection is now nonempty */
1042 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1044 memset(&cf2, 0, sizeof(CHARFORMAT2));
1045 cf2.cbSize = sizeof(CHARFORMAT2);
1046 cr.cpMin = 2; cr.cpMax = 6;
1047 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1048 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1050 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1051 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1052 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1053 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1055 DestroyWindow(hwndRichEdit);
1058 static void test_EM_SETTEXTMODE(void)
1060 HWND hwndRichEdit = new_richedit(NULL);
1061 CHARFORMAT2 cf2, cf2test;
1062 CHARRANGE cr;
1063 int rc = 0;
1065 /*Attempt to use mutually exclusive modes*/
1066 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT|TM_RICHTEXT, 0);
1067 ok(rc == E_INVALIDARG,
1068 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1070 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1071 /*Insert text into the control*/
1073 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1075 /*Attempt to change the control to plain text mode*/
1076 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1077 ok(rc == E_UNEXPECTED,
1078 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1080 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1081 If rich text is pasted, it should have the same formatting as the rest
1082 of the text in the control*/
1084 /*Italicize the text
1085 *NOTE: If the default text was already italicized, the test will simply
1086 reverse; in other words, it will copy a regular "wine" into a plain
1087 text window that uses an italicized format*/
1088 cf2.cbSize = sizeof(CHARFORMAT2);
1089 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1090 (LPARAM) &cf2);
1092 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1093 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1095 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1096 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1098 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1099 however, SCF_ALL has been implemented*/
1100 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1101 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1103 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1104 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1108 /*Select the string "wine"*/
1109 cr.cpMin = 0;
1110 cr.cpMax = 4;
1111 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1113 /*Copy the italicized "wine" to the clipboard*/
1114 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1116 /*Reset the formatting to default*/
1117 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1118 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1119 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1121 /*Clear the text in the control*/
1122 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1124 /*Switch to Plain Text Mode*/
1125 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1126 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1128 /*Input "wine" again in normal format*/
1129 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1131 /*Paste the italicized "wine" into the control*/
1132 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1134 /*Select a character from the first "wine" string*/
1135 cr.cpMin = 2;
1136 cr.cpMax = 3;
1137 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1139 /*Retrieve its formatting*/
1140 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1141 (LPARAM) &cf2);
1143 /*Select a character from the second "wine" string*/
1144 cr.cpMin = 5;
1145 cr.cpMax = 6;
1146 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1148 /*Retrieve its formatting*/
1149 cf2test.cbSize = sizeof(CHARFORMAT2);
1150 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1151 (LPARAM) &cf2test);
1153 /*Compare the two formattings*/
1154 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1155 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1156 cf2.dwEffects, cf2test.dwEffects);
1157 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1158 printing "wine" in the current format(normal)
1159 pasting "wine" from the clipboard(italicized)
1160 comparing the two formats(should differ)*/
1162 /*Attempt to switch with text in control*/
1163 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1164 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1166 /*Clear control*/
1167 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1169 /*Switch into Rich Text mode*/
1170 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1171 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1173 /*Print "wine" in normal formatting into the control*/
1174 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1176 /*Paste italicized "wine" into the control*/
1177 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1179 /*Select text from the first "wine" string*/
1180 cr.cpMin = 1;
1181 cr.cpMax = 3;
1182 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1184 /*Retrieve its formatting*/
1185 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1186 (LPARAM) &cf2);
1188 /*Select text from the second "wine" string*/
1189 cr.cpMin = 6;
1190 cr.cpMax = 7;
1191 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1193 /*Retrieve its formatting*/
1194 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1195 (LPARAM) &cf2test);
1197 /*Test that the two formattings are not the same*/
1198 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1199 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1200 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1202 DestroyWindow(hwndRichEdit);
1205 static void test_SETPARAFORMAT(void)
1207 HWND hwndRichEdit = new_richedit(NULL);
1208 PARAFORMAT2 fmt;
1209 HRESULT ret;
1210 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1211 fmt.cbSize = sizeof(PARAFORMAT2);
1212 fmt.dwMask = PFM_ALIGNMENT;
1213 fmt.wAlignment = PFA_LEFT;
1215 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1216 ok(ret != 0, "expected non-zero got %d\n", ret);
1218 fmt.cbSize = sizeof(PARAFORMAT2);
1219 fmt.dwMask = -1;
1220 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1221 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1222 * between richedit different native builds of riched20.dll
1223 * used on different Windows versions. */
1224 ret &= ~PFM_TABLEROWDELIMITER;
1225 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1227 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1228 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1230 DestroyWindow(hwndRichEdit);
1233 static void test_TM_PLAINTEXT(void)
1235 /*Tests plain text properties*/
1237 HWND hwndRichEdit = new_richedit(NULL);
1238 CHARFORMAT2 cf2, cf2test;
1239 CHARRANGE cr;
1240 int rc = 0;
1242 /*Switch to plain text mode*/
1244 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1245 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1247 /*Fill control with text*/
1249 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1251 /*Select some text and bold it*/
1253 cr.cpMin = 10;
1254 cr.cpMax = 20;
1255 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1256 cf2.cbSize = sizeof(CHARFORMAT2);
1257 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1259 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1260 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1262 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1263 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1265 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1266 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1268 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1269 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1271 /*Get the formatting of those characters*/
1273 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1275 /*Get the formatting of some other characters*/
1276 cf2test.cbSize = sizeof(CHARFORMAT2);
1277 cr.cpMin = 21;
1278 cr.cpMax = 30;
1279 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1280 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1282 /*Test that they are the same as plain text allows only one formatting*/
1284 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1285 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1286 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1288 /*Fill the control with a "wine" string, which when inserted will be bold*/
1290 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1292 /*Copy the bolded "wine" string*/
1294 cr.cpMin = 0;
1295 cr.cpMax = 4;
1296 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1297 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1299 /*Swap back to rich text*/
1301 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1302 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1304 /*Set the default formatting to bold italics*/
1306 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1307 cf2.dwMask |= CFM_ITALIC;
1308 cf2.dwEffects ^= CFE_ITALIC;
1309 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1310 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1312 /*Set the text in the control to "wine", which will be bold and italicized*/
1314 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1316 /*Paste the plain text "wine" string, which should take the insert
1317 formatting, which at the moment is bold italics*/
1319 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1321 /*Select the first "wine" string and retrieve its formatting*/
1323 cr.cpMin = 1;
1324 cr.cpMax = 3;
1325 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1326 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1328 /*Select the second "wine" string and retrieve its formatting*/
1330 cr.cpMin = 5;
1331 cr.cpMax = 7;
1332 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1333 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1335 /*Compare the two formattings. They should be the same.*/
1337 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1338 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1339 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1340 DestroyWindow(hwndRichEdit);
1343 static void test_WM_GETTEXT(void)
1345 HWND hwndRichEdit = new_richedit(NULL);
1346 static const char text[] = "Hello. My name is RichEdit!";
1347 static const char text2[] = "Hello. My name is RichEdit!\r";
1348 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1349 char buffer[1024] = {0};
1350 int result;
1352 /* Baseline test with normal-sized buffer */
1353 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1354 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1355 ok(result == lstrlen(buffer),
1356 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1357 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1358 result = strcmp(buffer,text);
1359 ok(result == 0,
1360 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1362 /* Test for returned value of WM_GETTEXTLENGTH */
1363 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1364 ok(result == lstrlen(text),
1365 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1366 result, lstrlen(text));
1368 /* Test for behavior in overflow case */
1369 memset(buffer, 0, 1024);
1370 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1371 ok(result == 0 ||
1372 result == lstrlenA(text) - 1, /* XP, win2k3 */
1373 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1374 result = strcmp(buffer,text);
1375 if (result)
1376 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1377 ok(result == 0,
1378 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1380 /* Baseline test with normal-sized buffer and carriage return */
1381 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1382 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1383 ok(result == lstrlen(buffer),
1384 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1385 result = strcmp(buffer,text2_after);
1386 ok(result == 0,
1387 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1389 /* Test for returned value of WM_GETTEXTLENGTH */
1390 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1391 ok(result == lstrlen(text2_after),
1392 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1393 result, lstrlen(text2_after));
1395 /* Test for behavior of CRLF conversion in case of overflow */
1396 memset(buffer, 0, 1024);
1397 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1398 ok(result == 0 ||
1399 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1400 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1401 result = strcmp(buffer,text2);
1402 if (result)
1403 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1404 ok(result == 0,
1405 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1407 DestroyWindow(hwndRichEdit);
1410 static void test_EM_GETTEXTRANGE(void)
1412 HWND hwndRichEdit = new_richedit(NULL);
1413 const char * text1 = "foo bar\r\nfoo bar";
1414 const char * text2 = "foo bar\rfoo bar";
1415 const char * expect = "bar\rfoo";
1416 char buffer[1024] = {0};
1417 LRESULT result;
1418 TEXTRANGEA textRange;
1420 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1422 textRange.lpstrText = buffer;
1423 textRange.chrg.cpMin = 4;
1424 textRange.chrg.cpMax = 11;
1425 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1426 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1427 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1431 textRange.lpstrText = buffer;
1432 textRange.chrg.cpMin = 4;
1433 textRange.chrg.cpMax = 11;
1434 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1435 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1436 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1438 /* cpMax of text length is used instead of -1 in this case */
1439 textRange.lpstrText = buffer;
1440 textRange.chrg.cpMin = 0;
1441 textRange.chrg.cpMax = -1;
1442 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1443 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1444 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1446 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1447 textRange.lpstrText = buffer;
1448 textRange.chrg.cpMin = -1;
1449 textRange.chrg.cpMax = 1;
1450 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1451 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1452 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1454 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1455 textRange.lpstrText = buffer;
1456 textRange.chrg.cpMin = 1;
1457 textRange.chrg.cpMax = -1;
1458 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1459 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1460 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1462 /* no end character is copied if cpMax - cpMin < 0 */
1463 textRange.lpstrText = buffer;
1464 textRange.chrg.cpMin = 5;
1465 textRange.chrg.cpMax = 5;
1466 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1467 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1468 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1470 /* cpMax of text length is used if cpMax > text length*/
1471 textRange.lpstrText = buffer;
1472 textRange.chrg.cpMin = 0;
1473 textRange.chrg.cpMax = 1000;
1474 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1475 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1476 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1478 DestroyWindow(hwndRichEdit);
1481 static void test_EM_GETSELTEXT(void)
1483 HWND hwndRichEdit = new_richedit(NULL);
1484 const char * text1 = "foo bar\r\nfoo bar";
1485 const char * text2 = "foo bar\rfoo bar";
1486 const char * expect = "bar\rfoo";
1487 char buffer[1024] = {0};
1488 LRESULT result;
1490 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1492 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1493 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1494 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1495 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1497 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1499 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1500 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1501 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1502 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1504 DestroyWindow(hwndRichEdit);
1507 /* FIXME: need to test unimplemented options and robustly test wparam */
1508 static void test_EM_SETOPTIONS(void)
1510 HWND hwndRichEdit;
1511 static const char text[] = "Hello. My name is RichEdit!";
1512 char buffer[1024] = {0};
1513 DWORD dwStyle, options, oldOptions;
1514 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1515 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1516 ES_SELECTIONBAR|ES_VERTICAL;
1518 /* Test initial options. */
1519 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1520 0, 0, 200, 60, NULL, NULL,
1521 hmoduleRichEdit, NULL);
1522 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1523 RICHEDIT_CLASS, (int) GetLastError());
1524 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1525 ok(options == 0, "Incorrect initial options %x\n", options);
1526 DestroyWindow(hwndRichEdit);
1528 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1529 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1530 0, 0, 200, 60, NULL, NULL,
1531 hmoduleRichEdit, NULL);
1532 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1533 RICHEDIT_CLASS, (int) GetLastError());
1534 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1535 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1536 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1537 "Incorrect initial options %x\n", options);
1539 /* NEGATIVE TESTING - NO OPTIONS SET */
1540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1541 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1543 /* testing no readonly by sending 'a' to the control*/
1544 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1545 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1546 ok(buffer[0]=='a',
1547 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1548 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1550 /* READONLY - sending 'a' to the control */
1551 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1552 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1553 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1554 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1555 ok(buffer[0]==text[0],
1556 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1558 /* EM_SETOPTIONS changes the window style, but changing the
1559 * window style does not change the options. */
1560 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1561 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1562 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1563 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1564 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1565 /* Confirm that the text is still read only. */
1566 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1567 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1568 ok(buffer[0]==text[0],
1569 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1571 oldOptions = options;
1572 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1573 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1574 ok(options == oldOptions,
1575 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1577 DestroyWindow(hwndRichEdit);
1580 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1582 CHARFORMAT2W text_format;
1583 text_format.cbSize = sizeof(text_format);
1584 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1585 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1586 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1589 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1591 int link_present = 0;
1593 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1594 if (is_url)
1595 { /* control text is url; should get CFE_LINK */
1596 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1598 else
1600 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1604 static HWND new_static_wnd(HWND parent) {
1605 return new_window("Static", 0, parent);
1608 static void test_EM_AUTOURLDETECT(void)
1610 /* DO NOT change the properties of the first two elements. To shorten the
1611 tests, all tests after WM_SETTEXT test just the first two elements -
1612 one non-URL and one URL */
1613 struct urls_s {
1614 const char *text;
1615 int is_url;
1616 } urls[12] = {
1617 {"winehq.org", 0},
1618 {"http://www.winehq.org", 1},
1619 {"http//winehq.org", 0},
1620 {"ww.winehq.org", 0},
1621 {"www.winehq.org", 1},
1622 {"ftp://192.168.1.1", 1},
1623 {"ftp//192.168.1.1", 0},
1624 {"mailto:your@email.com", 1},
1625 {"prospero:prosperoserver", 1},
1626 {"telnet:test", 1},
1627 {"news:newserver", 1},
1628 {"wais:waisserver", 1}
1631 int i, j;
1632 int urlRet=-1;
1633 HWND hwndRichEdit, parent;
1635 /* All of the following should cause the URL to be detected */
1636 const char * templates_delim[] = {
1637 "This is some text with X on it",
1638 "This is some text with (X) on it",
1639 "This is some text with X\r on it",
1640 "This is some text with ---X--- on it",
1641 "This is some text with \"X\" on it",
1642 "This is some text with 'X' on it",
1643 "This is some text with 'X' on it",
1644 "This is some text with :X: on it",
1646 "This text ends with X",
1648 "This is some text with X) on it",
1649 "This is some text with X--- on it",
1650 "This is some text with X\" on it",
1651 "This is some text with X' on it",
1652 "This is some text with X: on it",
1654 "This is some text with (X on it",
1655 "This is some text with \rX on it",
1656 "This is some text with ---X on it",
1657 "This is some text with \"X on it",
1658 "This is some text with 'X on it",
1659 "This is some text with :X on it",
1661 /* None of these should cause the URL to be detected */
1662 const char * templates_non_delim[] = {
1663 "This is some text with |X| on it",
1664 "This is some text with *X* on it",
1665 "This is some text with /X/ on it",
1666 "This is some text with +X+ on it",
1667 "This is some text with %X% on it",
1668 "This is some text with #X# on it",
1669 "This is some text with @X@ on it",
1670 "This is some text with \\X\\ on it",
1671 "This is some text with |X on it",
1672 "This is some text with *X on it",
1673 "This is some text with /X on it",
1674 "This is some text with +X on it",
1675 "This is some text with %X on it",
1676 "This is some text with #X on it",
1677 "This is some text with @X on it",
1678 "This is some text with \\X on it",
1680 /* All of these cause the URL detection to be extended by one more byte,
1681 thus demonstrating that the tested character is considered as part
1682 of the URL. */
1683 const char * templates_xten_delim[] = {
1684 "This is some text with X| on it",
1685 "This is some text with X* on it",
1686 "This is some text with X/ on it",
1687 "This is some text with X+ on it",
1688 "This is some text with X% on it",
1689 "This is some text with X# on it",
1690 "This is some text with X@ on it",
1691 "This is some text with X\\ on it",
1693 char buffer[1024];
1695 parent = new_static_wnd(NULL);
1696 hwndRichEdit = new_richedit(parent);
1697 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1698 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1699 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1700 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1701 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1702 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1703 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1704 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1705 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1706 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1707 /* for each url, check the text to see if CFE_LINK effect is present */
1708 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1710 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1711 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1712 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1714 /* Link detection should happen immediately upon WM_SETTEXT */
1715 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1716 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1717 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1719 DestroyWindow(hwndRichEdit);
1721 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1722 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1723 hwndRichEdit = new_richedit(parent);
1725 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1726 char * at_pos;
1727 int at_offset;
1728 int end_offset;
1730 at_pos = strchr(templates_delim[j], 'X');
1731 at_offset = at_pos - templates_delim[j];
1732 strncpy(buffer, templates_delim[j], at_offset);
1733 buffer[at_offset] = '\0';
1734 strcat(buffer, urls[i].text);
1735 strcat(buffer, templates_delim[j] + at_offset + 1);
1736 end_offset = at_offset + strlen(urls[i].text);
1738 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1741 /* This assumes no templates start with the URL itself, and that they
1742 have at least two characters before the URL text */
1743 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1744 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1745 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1746 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1747 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1748 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1750 if (urls[i].is_url)
1752 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1753 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1754 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1755 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, 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);
1764 if (buffer[end_offset] != '\0')
1766 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1767 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1768 if (buffer[end_offset +1] != '\0')
1770 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1771 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1776 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1777 char * at_pos;
1778 int at_offset;
1779 int end_offset;
1781 at_pos = strchr(templates_non_delim[j], 'X');
1782 at_offset = at_pos - templates_non_delim[j];
1783 strncpy(buffer, templates_non_delim[j], at_offset);
1784 buffer[at_offset] = '\0';
1785 strcat(buffer, urls[i].text);
1786 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1787 end_offset = at_offset + strlen(urls[i].text);
1789 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1790 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1792 /* This assumes no templates start with the URL itself, and that they
1793 have at least two characters before the URL text */
1794 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1795 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1796 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1797 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1798 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1799 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1801 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1802 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1803 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1804 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1805 if (buffer[end_offset] != '\0')
1807 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1808 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1809 if (buffer[end_offset +1] != '\0')
1811 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1812 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1817 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1818 char * at_pos;
1819 int at_offset;
1820 int end_offset;
1822 at_pos = strchr(templates_xten_delim[j], 'X');
1823 at_offset = at_pos - templates_xten_delim[j];
1824 strncpy(buffer, templates_xten_delim[j], at_offset);
1825 buffer[at_offset] = '\0';
1826 strcat(buffer, urls[i].text);
1827 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1828 end_offset = at_offset + strlen(urls[i].text);
1830 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1831 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1833 /* This assumes no templates start with the URL itself, and that they
1834 have at least two characters before the URL text */
1835 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1836 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1837 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1838 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1839 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1840 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1842 if (urls[i].is_url)
1844 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1845 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1846 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1847 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1848 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1849 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1851 else
1853 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1854 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1855 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1856 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1857 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1858 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1860 if (buffer[end_offset +1] != '\0')
1862 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1863 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1864 if (buffer[end_offset +2] != '\0')
1866 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1867 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1872 DestroyWindow(hwndRichEdit);
1873 hwndRichEdit = NULL;
1876 /* Test detection of URLs within normal text - WM_CHAR case. */
1877 /* Test only the first two URL examples for brevity */
1878 for (i = 0; i < 2; i++) {
1879 hwndRichEdit = new_richedit(parent);
1881 /* Also for brevity, test only the first three delimiters */
1882 for (j = 0; j < 3; j++) {
1883 char * at_pos;
1884 int at_offset;
1885 int end_offset;
1886 int u, v;
1888 at_pos = strchr(templates_delim[j], 'X');
1889 at_offset = at_pos - templates_delim[j];
1890 end_offset = at_offset + strlen(urls[i].text);
1892 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1893 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1894 for (u = 0; templates_delim[j][u]; u++) {
1895 if (templates_delim[j][u] == '\r') {
1896 simulate_typing_characters(hwndRichEdit, "\r");
1897 } else if (templates_delim[j][u] != 'X') {
1898 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1899 } else {
1900 for (v = 0; urls[i].text[v]; v++) {
1901 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1905 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1907 /* This assumes no templates start with the URL itself, and that they
1908 have at least two characters before the URL text */
1909 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1910 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1911 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1912 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1913 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1914 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1916 if (urls[i].is_url)
1918 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1919 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1920 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1921 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1923 else
1925 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1926 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1927 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1928 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1930 if (buffer[end_offset] != '\0')
1932 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1933 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1934 if (buffer[end_offset +1] != '\0')
1936 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1937 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1941 /* The following will insert a paragraph break after the first character
1942 of the URL candidate, thus breaking the URL. It is expected that the
1943 CFE_LINK attribute should break across both pieces of the URL */
1944 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1945 simulate_typing_characters(hwndRichEdit, "\r");
1946 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1948 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1949 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1950 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1951 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1952 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1953 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1955 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1956 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1957 /* end_offset moved because of paragraph break */
1958 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1959 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1960 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1961 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1963 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1964 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1965 if (buffer[end_offset +2] != '\0')
1967 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1968 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1972 /* The following will remove the just-inserted paragraph break, thus
1973 restoring the URL */
1974 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1975 simulate_typing_characters(hwndRichEdit, "\b");
1976 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1978 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1979 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1980 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1981 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1982 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1983 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1985 if (urls[i].is_url)
1987 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1988 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1989 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1990 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1992 else
1994 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1995 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1996 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1997 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1999 if (buffer[end_offset] != '\0')
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2003 if (buffer[end_offset +1] != '\0')
2005 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2006 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2010 DestroyWindow(hwndRichEdit);
2011 hwndRichEdit = NULL;
2014 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2015 /* Test just the first two URL examples for brevity */
2016 for (i = 0; i < 2; i++) {
2017 SETTEXTEX st;
2019 hwndRichEdit = new_richedit(parent);
2021 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2022 be detected:
2023 1) Set entire text, a la WM_SETTEXT
2024 2) Set a selection of the text to the URL
2025 3) Set a portion of the text at a time, which eventually results in
2026 an URL
2027 All of them should give equivalent results
2030 /* Set entire text in one go, like WM_SETTEXT */
2031 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2032 char * at_pos;
2033 int at_offset;
2034 int end_offset;
2036 st.codepage = CP_ACP;
2037 st.flags = ST_DEFAULT;
2039 at_pos = strchr(templates_delim[j], 'X');
2040 at_offset = at_pos - templates_delim[j];
2041 strncpy(buffer, templates_delim[j], at_offset);
2042 buffer[at_offset] = '\0';
2043 strcat(buffer, urls[i].text);
2044 strcat(buffer, templates_delim[j] + at_offset + 1);
2045 end_offset = at_offset + strlen(urls[i].text);
2047 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2048 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2050 /* This assumes no templates start with the URL itself, and that they
2051 have at least two characters before the URL text */
2052 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2053 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2054 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2055 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2056 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2057 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2059 if (urls[i].is_url)
2061 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2062 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2063 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2064 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2066 else
2068 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2069 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2070 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2071 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2073 if (buffer[end_offset] != '\0')
2075 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2076 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2077 if (buffer[end_offset +1] != '\0')
2079 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2080 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2085 /* Set selection with X to the URL */
2086 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2087 char * at_pos;
2088 int at_offset;
2089 int end_offset;
2091 at_pos = strchr(templates_delim[j], 'X');
2092 at_offset = at_pos - templates_delim[j];
2093 end_offset = at_offset + strlen(urls[i].text);
2095 st.codepage = CP_ACP;
2096 st.flags = ST_DEFAULT;
2097 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2098 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2099 st.flags = ST_SELECTION;
2100 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2101 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2102 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2104 /* This assumes no templates start with the URL itself, and that they
2105 have at least two characters before the URL text */
2106 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2107 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2108 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2109 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2110 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2111 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2113 if (urls[i].is_url)
2115 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2116 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2117 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2118 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2120 else
2122 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2123 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2124 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2125 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2127 if (buffer[end_offset] != '\0')
2129 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2130 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2131 if (buffer[end_offset +1] != '\0')
2133 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2134 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2139 /* Set selection with X to the first character of the URL, then the rest */
2140 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2141 char * at_pos;
2142 int at_offset;
2143 int end_offset;
2145 at_pos = strchr(templates_delim[j], 'X');
2146 at_offset = at_pos - templates_delim[j];
2147 end_offset = at_offset + strlen(urls[i].text);
2149 strcpy(buffer, "YY");
2150 buffer[0] = urls[i].text[0];
2152 st.codepage = CP_ACP;
2153 st.flags = ST_DEFAULT;
2154 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2155 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2156 st.flags = ST_SELECTION;
2157 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2158 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2159 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2160 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2161 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2163 /* This assumes no templates start with the URL itself, and that they
2164 have at least two characters before the URL text */
2165 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2166 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2167 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2168 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2169 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2170 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2172 if (urls[i].is_url)
2174 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2175 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2176 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2177 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2179 else
2181 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2182 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2183 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2184 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2186 if (buffer[end_offset] != '\0')
2188 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2189 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2190 if (buffer[end_offset +1] != '\0')
2192 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2193 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2198 DestroyWindow(hwndRichEdit);
2199 hwndRichEdit = NULL;
2202 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2203 /* Test just the first two URL examples for brevity */
2204 for (i = 0; i < 2; i++) {
2205 hwndRichEdit = new_richedit(parent);
2207 /* Set selection with X to the URL */
2208 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2209 char * at_pos;
2210 int at_offset;
2211 int end_offset;
2213 at_pos = strchr(templates_delim[j], 'X');
2214 at_offset = at_pos - templates_delim[j];
2215 end_offset = at_offset + strlen(urls[i].text);
2217 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2218 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2219 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2220 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2221 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2223 /* This assumes no templates start with the URL itself, and that they
2224 have at least two characters before the URL text */
2225 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2226 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2227 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2228 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2229 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2230 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2232 if (urls[i].is_url)
2234 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2235 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2236 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2237 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2239 else
2241 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2242 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2243 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2244 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2246 if (buffer[end_offset] != '\0')
2248 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2249 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2250 if (buffer[end_offset +1] != '\0')
2252 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2253 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2258 /* Set selection with X to the first character of the URL, then the rest */
2259 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2260 char * at_pos;
2261 int at_offset;
2262 int end_offset;
2264 at_pos = strchr(templates_delim[j], 'X');
2265 at_offset = at_pos - templates_delim[j];
2266 end_offset = at_offset + strlen(urls[i].text);
2268 strcpy(buffer, "YY");
2269 buffer[0] = urls[i].text[0];
2271 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2272 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2273 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2274 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2275 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2276 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2277 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2279 /* This assumes no templates start with the URL itself, and that they
2280 have at least two characters before the URL text */
2281 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2282 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2283 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2284 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2285 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2286 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2288 if (urls[i].is_url)
2290 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2291 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2292 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2293 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2295 else
2297 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2298 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2299 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2300 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2302 if (buffer[end_offset] != '\0')
2304 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2305 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2306 if (buffer[end_offset +1] != '\0')
2308 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2309 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2314 DestroyWindow(hwndRichEdit);
2315 hwndRichEdit = NULL;
2318 DestroyWindow(parent);
2321 static void test_EM_SCROLL(void)
2323 int i, j;
2324 int r; /* return value */
2325 int expr; /* expected return value */
2326 HWND hwndRichEdit = new_richedit(NULL);
2327 int y_before, y_after; /* units of lines of text */
2329 /* test a richedit box containing a single line of text */
2330 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2331 expr = 0x00010000;
2332 for (i = 0; i < 4; i++) {
2333 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2335 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2336 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2337 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2338 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2339 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2340 "(i == %d)\n", y_after, i);
2344 * test a richedit box that will scroll. There are two general
2345 * cases: the case without any long lines and the case with a long
2346 * line.
2348 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2349 if (i == 0)
2350 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2351 else
2352 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2353 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2354 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2355 "LONG LINE \nb\nc\nd\ne");
2356 for (j = 0; j < 12; j++) /* reset scroll position to top */
2357 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2359 /* get first visible line */
2360 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2361 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2363 /* get new current first visible line */
2364 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2366 ok(((r & 0xffffff00) == 0x00010000) &&
2367 ((r & 0x000000ff) != 0x00000000),
2368 "EM_SCROLL page down didn't scroll by a small positive number of "
2369 "lines (r == 0x%08x)\n", r);
2370 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2371 "(line %d scrolled to line %d\n", y_before, y_after);
2373 y_before = y_after;
2375 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2376 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2377 ok(((r & 0xffffff00) == 0x0001ff00),
2378 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2379 "(r == 0x%08x)\n", r);
2380 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2381 "%d scrolled to line %d\n", y_before, y_after);
2383 y_before = y_after;
2385 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2387 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2389 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2390 "(r == 0x%08x)\n", r);
2391 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2392 "1 line (%d scrolled to %d)\n", y_before, y_after);
2394 y_before = y_after;
2396 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2398 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2400 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2401 "(r == 0x%08x)\n", r);
2402 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2403 "line (%d scrolled to %d)\n", y_before, y_after);
2405 y_before = y_after;
2407 r = SendMessage(hwndRichEdit, EM_SCROLL,
2408 SB_LINEUP, 0); /* lineup beyond top */
2410 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2412 ok(r == 0x00010000,
2413 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2414 ok(y_before == y_after,
2415 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2417 y_before = y_after;
2419 r = SendMessage(hwndRichEdit, EM_SCROLL,
2420 SB_PAGEUP, 0);/*page up beyond top */
2422 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2424 ok(r == 0x00010000,
2425 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2426 ok(y_before == y_after,
2427 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2429 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2430 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2431 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2432 r = SendMessage(hwndRichEdit, EM_SCROLL,
2433 SB_PAGEDOWN, 0); /* page down beyond bot */
2434 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2436 ok(r == 0x00010000,
2437 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2438 ok(y_before == y_after,
2439 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2440 y_before, y_after);
2442 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2443 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2444 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2446 ok(r == 0x00010000,
2447 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2448 ok(y_before == y_after,
2449 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2450 y_before, y_after);
2452 DestroyWindow(hwndRichEdit);
2455 static unsigned int recursionLevel = 0;
2456 static unsigned int WM_SIZE_recursionLevel = 0;
2457 static BOOL bailedOutOfRecursion = FALSE;
2458 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2460 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2462 LRESULT r;
2464 if (bailedOutOfRecursion) return 0;
2465 if (recursionLevel >= 32) {
2466 bailedOutOfRecursion = TRUE;
2467 return 0;
2470 recursionLevel++;
2471 switch (message) {
2472 case WM_SIZE:
2473 WM_SIZE_recursionLevel++;
2474 r = richeditProc(hwnd, message, wParam, lParam);
2475 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2476 ShowScrollBar(hwnd, SB_VERT, TRUE);
2477 WM_SIZE_recursionLevel--;
2478 break;
2479 default:
2480 r = richeditProc(hwnd, message, wParam, lParam);
2481 break;
2483 recursionLevel--;
2484 return r;
2487 static void test_scrollbar_visibility(void)
2489 HWND hwndRichEdit;
2490 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2491 SCROLLINFO si;
2492 WNDCLASSA cls;
2493 BOOL r;
2495 /* These tests show that richedit should temporarily refrain from automatically
2496 hiding or showing its scrollbars (vertical at least) when an explicit request
2497 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2498 Some applications depend on forced showing (when otherwise richedit would
2499 hide the vertical scrollbar) and are thrown on an endless recursive loop
2500 if richedit auto-hides the scrollbar again. Apparently they never heard of
2501 the ES_DISABLENOSCROLL style... */
2503 hwndRichEdit = new_richedit(NULL);
2505 /* Test default scrollbar visibility behavior */
2506 memset(&si, 0, sizeof(si));
2507 si.cbSize = sizeof(si);
2508 si.fMask = SIF_PAGE | SIF_RANGE;
2509 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2510 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2511 "Vertical scrollbar is visible, should be invisible.\n");
2512 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2513 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2514 si.nPage, si.nMin, si.nMax);
2516 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2517 memset(&si, 0, sizeof(si));
2518 si.cbSize = sizeof(si);
2519 si.fMask = SIF_PAGE | SIF_RANGE;
2520 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2521 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2522 "Vertical scrollbar is visible, should be invisible.\n");
2523 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2524 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2525 si.nPage, si.nMin, si.nMax);
2527 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2528 memset(&si, 0, sizeof(si));
2529 si.cbSize = sizeof(si);
2530 si.fMask = SIF_PAGE | SIF_RANGE;
2531 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2532 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2533 "Vertical scrollbar is invisible, should be visible.\n");
2534 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2535 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2536 si.nPage, si.nMin, si.nMax);
2538 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2539 even though it hides the scrollbar */
2540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2541 memset(&si, 0, sizeof(si));
2542 si.cbSize = sizeof(si);
2543 si.fMask = SIF_PAGE | SIF_RANGE;
2544 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2545 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2546 "Vertical scrollbar is visible, should be invisible.\n");
2547 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2548 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2549 si.nPage, si.nMin, si.nMax);
2551 /* Setting non-scrolling text again does *not* reset scrollbar range */
2552 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2553 memset(&si, 0, sizeof(si));
2554 si.cbSize = sizeof(si);
2555 si.fMask = SIF_PAGE | SIF_RANGE;
2556 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2557 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2558 "Vertical scrollbar is visible, should be invisible.\n");
2559 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2560 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2561 si.nPage, si.nMin, si.nMax);
2563 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2564 memset(&si, 0, sizeof(si));
2565 si.cbSize = sizeof(si);
2566 si.fMask = SIF_PAGE | SIF_RANGE;
2567 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2568 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2569 "Vertical scrollbar is visible, should be invisible.\n");
2570 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2571 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2572 si.nPage, si.nMin, si.nMax);
2574 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2575 memset(&si, 0, sizeof(si));
2576 si.cbSize = sizeof(si);
2577 si.fMask = SIF_PAGE | SIF_RANGE;
2578 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2579 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2580 "Vertical scrollbar is visible, should be invisible.\n");
2581 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2582 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2583 si.nPage, si.nMin, si.nMax);
2585 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2586 memset(&si, 0, sizeof(si));
2587 si.cbSize = sizeof(si);
2588 si.fMask = SIF_PAGE | SIF_RANGE;
2589 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2590 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2591 "Vertical scrollbar is visible, should be invisible.\n");
2592 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2593 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2594 si.nPage, si.nMin, si.nMax);
2596 DestroyWindow(hwndRichEdit);
2598 /* Test again, with ES_DISABLENOSCROLL style */
2599 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2601 /* Test default scrollbar visibility behavior */
2602 memset(&si, 0, sizeof(si));
2603 si.cbSize = sizeof(si);
2604 si.fMask = SIF_PAGE | SIF_RANGE;
2605 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2606 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2607 "Vertical scrollbar is invisible, should be visible.\n");
2608 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2609 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2610 si.nPage, si.nMin, si.nMax);
2612 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2613 memset(&si, 0, sizeof(si));
2614 si.cbSize = sizeof(si);
2615 si.fMask = SIF_PAGE | SIF_RANGE;
2616 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2617 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2618 "Vertical scrollbar is invisible, should be visible.\n");
2619 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2620 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2621 si.nPage, si.nMin, si.nMax);
2623 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2624 memset(&si, 0, sizeof(si));
2625 si.cbSize = sizeof(si);
2626 si.fMask = SIF_PAGE | SIF_RANGE;
2627 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2628 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2629 "Vertical scrollbar is invisible, should be visible.\n");
2630 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2631 "reported page/range is %d (%d..%d)\n",
2632 si.nPage, si.nMin, si.nMax);
2634 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2635 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2636 memset(&si, 0, sizeof(si));
2637 si.cbSize = sizeof(si);
2638 si.fMask = SIF_PAGE | SIF_RANGE;
2639 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2640 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2641 "Vertical scrollbar is invisible, should be visible.\n");
2642 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2643 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2644 si.nPage, si.nMin, si.nMax);
2646 /* Setting non-scrolling text again does *not* reset scrollbar range */
2647 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2648 memset(&si, 0, sizeof(si));
2649 si.cbSize = sizeof(si);
2650 si.fMask = SIF_PAGE | SIF_RANGE;
2651 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2652 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2653 "Vertical scrollbar is invisible, should be visible.\n");
2654 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2655 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2656 si.nPage, si.nMin, si.nMax);
2658 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2659 memset(&si, 0, sizeof(si));
2660 si.cbSize = sizeof(si);
2661 si.fMask = SIF_PAGE | SIF_RANGE;
2662 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2663 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2664 "Vertical scrollbar is invisible, should be visible.\n");
2665 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2666 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2667 si.nPage, si.nMin, si.nMax);
2669 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2670 memset(&si, 0, sizeof(si));
2671 si.cbSize = sizeof(si);
2672 si.fMask = SIF_PAGE | SIF_RANGE;
2673 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2674 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2675 "Vertical scrollbar is invisible, should be visible.\n");
2676 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2677 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2678 si.nPage, si.nMin, si.nMax);
2680 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2681 memset(&si, 0, sizeof(si));
2682 si.cbSize = sizeof(si);
2683 si.fMask = SIF_PAGE | SIF_RANGE;
2684 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2685 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2686 "Vertical scrollbar is invisible, should be visible.\n");
2687 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2688 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2689 si.nPage, si.nMin, si.nMax);
2691 DestroyWindow(hwndRichEdit);
2693 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2694 hwndRichEdit = new_richedit(NULL);
2696 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2697 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2698 memset(&si, 0, sizeof(si));
2699 si.cbSize = sizeof(si);
2700 si.fMask = SIF_PAGE | SIF_RANGE;
2701 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2702 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2703 "Vertical scrollbar is invisible, should be visible.\n");
2704 todo_wine {
2705 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2706 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2707 si.nPage, si.nMin, si.nMax);
2710 /* Ditto, see above */
2711 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
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 invisible, should be visible.\n");
2718 todo_wine {
2719 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2720 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2721 si.nPage, si.nMin, si.nMax);
2724 /* Ditto, see above */
2725 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2726 memset(&si, 0, sizeof(si));
2727 si.cbSize = sizeof(si);
2728 si.fMask = SIF_PAGE | SIF_RANGE;
2729 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2730 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2731 "Vertical scrollbar is invisible, should be visible.\n");
2732 todo_wine {
2733 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2734 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2735 si.nPage, si.nMin, si.nMax);
2738 /* Ditto, see above */
2739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2740 memset(&si, 0, sizeof(si));
2741 si.cbSize = sizeof(si);
2742 si.fMask = SIF_PAGE | SIF_RANGE;
2743 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2744 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2745 "Vertical scrollbar is invisible, should be visible.\n");
2746 todo_wine {
2747 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2748 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2749 si.nPage, si.nMin, si.nMax);
2752 /* Ditto, see above */
2753 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2754 memset(&si, 0, sizeof(si));
2755 si.cbSize = sizeof(si);
2756 si.fMask = SIF_PAGE | SIF_RANGE;
2757 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2758 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2759 "Vertical scrollbar is invisible, should be visible.\n");
2760 todo_wine {
2761 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2762 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2763 si.nPage, si.nMin, si.nMax);
2766 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2767 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2768 memset(&si, 0, sizeof(si));
2769 si.cbSize = sizeof(si);
2770 si.fMask = SIF_PAGE | SIF_RANGE;
2771 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2772 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2773 "Vertical scrollbar is visible, should be invisible.\n");
2774 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2775 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2776 si.nPage, si.nMin, si.nMax);
2778 DestroyWindow(hwndRichEdit);
2780 hwndRichEdit = new_richedit(NULL);
2782 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2783 memset(&si, 0, sizeof(si));
2784 si.cbSize = sizeof(si);
2785 si.fMask = SIF_PAGE | SIF_RANGE;
2786 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2787 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2788 "Vertical scrollbar is visible, should be invisible.\n");
2789 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2790 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2791 si.nPage, si.nMin, si.nMax);
2793 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2794 memset(&si, 0, sizeof(si));
2795 si.cbSize = sizeof(si);
2796 si.fMask = SIF_PAGE | SIF_RANGE;
2797 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2798 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2799 "Vertical scrollbar is visible, should be invisible.\n");
2800 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2801 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2802 si.nPage, si.nMin, si.nMax);
2804 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2805 memset(&si, 0, sizeof(si));
2806 si.cbSize = sizeof(si);
2807 si.fMask = SIF_PAGE | SIF_RANGE;
2808 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2809 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2810 "Vertical scrollbar is visible, should be invisible.\n");
2811 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2812 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2813 si.nPage, si.nMin, si.nMax);
2815 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2816 memset(&si, 0, sizeof(si));
2817 si.cbSize = sizeof(si);
2818 si.fMask = SIF_PAGE | SIF_RANGE;
2819 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2820 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2821 "Vertical scrollbar is visible, should be invisible.\n");
2822 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2823 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2824 si.nPage, si.nMin, si.nMax);
2826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2827 memset(&si, 0, sizeof(si));
2828 si.cbSize = sizeof(si);
2829 si.fMask = SIF_PAGE | SIF_RANGE;
2830 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2832 "Vertical scrollbar is invisible, should be visible.\n");
2833 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2834 "reported page/range is %d (%d..%d)\n",
2835 si.nPage, si.nMin, si.nMax);
2837 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2838 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2839 memset(&si, 0, sizeof(si));
2840 si.cbSize = sizeof(si);
2841 si.fMask = SIF_PAGE | SIF_RANGE;
2842 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2843 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2844 "Vertical scrollbar is visible, should be invisible.\n");
2845 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2846 "reported page/range is %d (%d..%d)\n",
2847 si.nPage, si.nMin, si.nMax);
2849 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2850 memset(&si, 0, sizeof(si));
2851 si.cbSize = sizeof(si);
2852 si.fMask = SIF_PAGE | SIF_RANGE;
2853 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2854 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2855 "Vertical scrollbar is visible, should be invisible.\n");
2856 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2857 "reported page/range is %d (%d..%d)\n",
2858 si.nPage, si.nMin, si.nMax);
2860 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2861 EM_SCROLL will make visible any forcefully invisible scrollbar */
2862 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2863 memset(&si, 0, sizeof(si));
2864 si.cbSize = sizeof(si);
2865 si.fMask = SIF_PAGE | SIF_RANGE;
2866 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2867 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2868 "Vertical scrollbar is invisible, should be visible.\n");
2869 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2870 "reported page/range is %d (%d..%d)\n",
2871 si.nPage, si.nMin, si.nMax);
2873 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
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 visible, should be invisible.\n");
2880 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2881 "reported page/range is %d (%d..%d)\n",
2882 si.nPage, si.nMin, si.nMax);
2884 /* Again, EM_SCROLL, with SB_LINEUP */
2885 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 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,
2893 "reported page/range is %d (%d..%d)\n",
2894 si.nPage, si.nMin, si.nMax);
2896 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2897 memset(&si, 0, sizeof(si));
2898 si.cbSize = sizeof(si);
2899 si.fMask = SIF_PAGE | SIF_RANGE;
2900 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2901 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2902 "Vertical scrollbar is visible, should be invisible.\n");
2903 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2904 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2905 si.nPage, si.nMin, si.nMax);
2907 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2908 memset(&si, 0, sizeof(si));
2909 si.cbSize = sizeof(si);
2910 si.fMask = SIF_PAGE | SIF_RANGE;
2911 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2912 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2913 "Vertical scrollbar is invisible, should be visible.\n");
2914 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2915 "reported page/range is %d (%d..%d)\n",
2916 si.nPage, si.nMin, si.nMax);
2918 DestroyWindow(hwndRichEdit);
2921 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2922 hwndRichEdit = new_richedit(NULL);
2924 #define ENABLE_WS_VSCROLL(hwnd) \
2925 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2926 #define DISABLE_WS_VSCROLL(hwnd) \
2927 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2929 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2930 ENABLE_WS_VSCROLL(hwndRichEdit);
2931 memset(&si, 0, sizeof(si));
2932 si.cbSize = sizeof(si);
2933 si.fMask = SIF_PAGE | SIF_RANGE;
2934 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2935 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2936 "Vertical scrollbar is invisible, should be visible.\n");
2937 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2938 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2939 si.nPage, si.nMin, si.nMax);
2941 /* Ditto, see above */
2942 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2943 memset(&si, 0, sizeof(si));
2944 si.cbSize = sizeof(si);
2945 si.fMask = SIF_PAGE | SIF_RANGE;
2946 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2947 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2948 "Vertical scrollbar is invisible, should be visible.\n");
2949 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2950 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2951 si.nPage, si.nMin, si.nMax);
2953 /* Ditto, see above */
2954 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2955 memset(&si, 0, sizeof(si));
2956 si.cbSize = sizeof(si);
2957 si.fMask = SIF_PAGE | SIF_RANGE;
2958 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2959 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2960 "Vertical scrollbar is invisible, should be visible.\n");
2961 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2962 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2963 si.nPage, si.nMin, si.nMax);
2965 /* Ditto, see above */
2966 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2967 memset(&si, 0, sizeof(si));
2968 si.cbSize = sizeof(si);
2969 si.fMask = SIF_PAGE | SIF_RANGE;
2970 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2971 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2972 "Vertical scrollbar is invisible, should be visible.\n");
2973 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2974 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2975 si.nPage, si.nMin, si.nMax);
2977 /* Ditto, see above */
2978 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2979 memset(&si, 0, sizeof(si));
2980 si.cbSize = sizeof(si);
2981 si.fMask = SIF_PAGE | SIF_RANGE;
2982 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2983 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2984 "Vertical scrollbar is invisible, should be visible.\n");
2985 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2986 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2987 si.nPage, si.nMin, si.nMax);
2989 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2990 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
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 visible, should be invisible.\n");
2997 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2998 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2999 si.nPage, si.nMin, si.nMax);
3001 DestroyWindow(hwndRichEdit);
3003 hwndRichEdit = new_richedit(NULL);
3005 DISABLE_WS_VSCROLL(hwndRichEdit);
3006 memset(&si, 0, sizeof(si));
3007 si.cbSize = sizeof(si);
3008 si.fMask = SIF_PAGE | SIF_RANGE;
3009 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3010 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3011 "Vertical scrollbar is visible, should be invisible.\n");
3012 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3013 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3014 si.nPage, si.nMin, si.nMax);
3016 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3017 memset(&si, 0, sizeof(si));
3018 si.cbSize = sizeof(si);
3019 si.fMask = SIF_PAGE | SIF_RANGE;
3020 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3021 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3022 "Vertical scrollbar is visible, should be invisible.\n");
3023 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3024 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3025 si.nPage, si.nMin, si.nMax);
3027 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3028 memset(&si, 0, sizeof(si));
3029 si.cbSize = sizeof(si);
3030 si.fMask = SIF_PAGE | SIF_RANGE;
3031 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3032 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3033 "Vertical scrollbar is visible, should be invisible.\n");
3034 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3035 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3036 si.nPage, si.nMin, si.nMax);
3038 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3039 memset(&si, 0, sizeof(si));
3040 si.cbSize = sizeof(si);
3041 si.fMask = SIF_PAGE | SIF_RANGE;
3042 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3043 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3044 "Vertical scrollbar is visible, should be invisible.\n");
3045 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3046 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3047 si.nPage, si.nMin, si.nMax);
3049 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3050 memset(&si, 0, sizeof(si));
3051 si.cbSize = sizeof(si);
3052 si.fMask = SIF_PAGE | SIF_RANGE;
3053 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3054 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3055 "Vertical scrollbar is invisible, should be visible.\n");
3056 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3057 "reported page/range is %d (%d..%d)\n",
3058 si.nPage, si.nMin, si.nMax);
3060 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3061 DISABLE_WS_VSCROLL(hwndRichEdit);
3062 memset(&si, 0, sizeof(si));
3063 si.cbSize = sizeof(si);
3064 si.fMask = SIF_PAGE | SIF_RANGE;
3065 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3066 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3067 "Vertical scrollbar is visible, should be invisible.\n");
3068 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3069 "reported page/range is %d (%d..%d)\n",
3070 si.nPage, si.nMin, si.nMax);
3072 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3073 memset(&si, 0, sizeof(si));
3074 si.cbSize = sizeof(si);
3075 si.fMask = SIF_PAGE | SIF_RANGE;
3076 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3077 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3078 "Vertical scrollbar is visible, should be invisible.\n");
3079 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3080 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3081 si.nPage, si.nMin, si.nMax);
3083 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3084 memset(&si, 0, sizeof(si));
3085 si.cbSize = sizeof(si);
3086 si.fMask = SIF_PAGE | SIF_RANGE;
3087 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3088 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3089 "Vertical scrollbar is invisible, should be visible.\n");
3090 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3091 "reported page/range is %d (%d..%d)\n",
3092 si.nPage, si.nMin, si.nMax);
3094 DISABLE_WS_VSCROLL(hwndRichEdit);
3095 memset(&si, 0, sizeof(si));
3096 si.cbSize = sizeof(si);
3097 si.fMask = SIF_PAGE | SIF_RANGE;
3098 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3099 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3100 "Vertical scrollbar is visible, should be invisible.\n");
3101 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3102 "reported page/range is %d (%d..%d)\n",
3103 si.nPage, si.nMin, si.nMax);
3105 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3106 EM_SCROLL will make visible any forcefully invisible scrollbar */
3107 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3108 memset(&si, 0, sizeof(si));
3109 si.cbSize = sizeof(si);
3110 si.fMask = SIF_PAGE | SIF_RANGE;
3111 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3112 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3113 "Vertical scrollbar is invisible, should be visible.\n");
3114 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3115 "reported page/range is %d (%d..%d)\n",
3116 si.nPage, si.nMin, si.nMax);
3118 DISABLE_WS_VSCROLL(hwndRichEdit);
3119 memset(&si, 0, sizeof(si));
3120 si.cbSize = sizeof(si);
3121 si.fMask = SIF_PAGE | SIF_RANGE;
3122 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3123 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3124 "Vertical scrollbar is visible, should be invisible.\n");
3125 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3126 "reported page/range is %d (%d..%d)\n",
3127 si.nPage, si.nMin, si.nMax);
3129 /* Again, EM_SCROLL, with SB_LINEUP */
3130 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3131 memset(&si, 0, sizeof(si));
3132 si.cbSize = sizeof(si);
3133 si.fMask = SIF_PAGE | SIF_RANGE;
3134 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3135 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3136 "Vertical scrollbar is invisible, should be visible.\n");
3137 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3138 "reported page/range is %d (%d..%d)\n",
3139 si.nPage, si.nMin, si.nMax);
3141 DestroyWindow(hwndRichEdit);
3143 /* This window proc models what is going on with Corman Lisp 3.0.
3144 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3145 force the scrollbar into visibility. Recursion should NOT happen
3146 as a result of this action.
3148 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3149 if (r) {
3150 richeditProc = cls.lpfnWndProc;
3151 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3152 cls.lpszClassName = "RicheditStupidOverride";
3153 if(!RegisterClassA(&cls)) assert(0);
3155 recursionLevel = 0;
3156 WM_SIZE_recursionLevel = 0;
3157 bailedOutOfRecursion = FALSE;
3158 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3159 ok(!bailedOutOfRecursion,
3160 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3162 recursionLevel = 0;
3163 WM_SIZE_recursionLevel = 0;
3164 bailedOutOfRecursion = FALSE;
3165 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3166 ok(!bailedOutOfRecursion,
3167 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3169 /* Unblock window in order to process WM_DESTROY */
3170 recursionLevel = 0;
3171 bailedOutOfRecursion = FALSE;
3172 WM_SIZE_recursionLevel = 0;
3173 DestroyWindow(hwndRichEdit);
3177 static void test_EM_SETUNDOLIMIT(void)
3179 /* cases we test for:
3180 * default behaviour - limiting at 100 undo's
3181 * undo disabled - setting a limit of 0
3182 * undo limited - undo limit set to some to some number, like 2
3183 * bad input - sending a negative number should default to 100 undo's */
3185 HWND hwndRichEdit = new_richedit(NULL);
3186 CHARRANGE cr;
3187 int i;
3188 int result;
3190 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3191 cr.cpMin = 0;
3192 cr.cpMax = 1;
3193 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3194 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3195 also, multiple pastes don't combine like WM_CHAR would */
3196 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3198 /* first case - check the default */
3199 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3200 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3201 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3202 for (i=0; i<100; i++) /* Undo 100 of them */
3203 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3204 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3205 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3207 /* second case - cannot undo */
3208 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3209 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3210 SendMessage(hwndRichEdit,
3211 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3212 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3213 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3215 /* third case - set it to an arbitrary number */
3216 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3217 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3218 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3219 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3220 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3221 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3222 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3223 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3224 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3225 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3226 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3227 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3228 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3229 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3231 /* fourth case - setting negative numbers should default to 100 undos */
3232 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3233 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3234 ok (result == 100,
3235 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3237 DestroyWindow(hwndRichEdit);
3240 static void test_ES_PASSWORD(void)
3242 /* This isn't hugely testable, so we're just going to run it through its paces */
3244 HWND hwndRichEdit = new_richedit(NULL);
3245 WCHAR result;
3247 /* First, check the default of a regular control */
3248 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3249 ok (result == 0,
3250 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3252 /* Now, set it to something normal */
3253 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3254 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3255 ok (result == 120,
3256 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3258 /* Now, set it to something odd */
3259 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3260 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3261 ok (result == 1234,
3262 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3263 DestroyWindow(hwndRichEdit);
3266 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3267 LPBYTE pbBuff,
3268 LONG cb,
3269 LONG *pcb)
3271 char** str = (char**)dwCookie;
3272 *pcb = cb;
3273 if (*pcb > 0) {
3274 memcpy(*str, pbBuff, *pcb);
3275 *str += *pcb;
3277 return 0;
3280 static void test_WM_SETTEXT(void)
3282 HWND hwndRichEdit = new_richedit(NULL);
3283 const char * TestItem1 = "TestSomeText";
3284 const char * TestItem2 = "TestSomeText\r";
3285 const char * TestItem2_after = "TestSomeText\r\n";
3286 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3287 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3288 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3289 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3290 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3291 const char * TestItem5_after = "TestSomeText TestSomeText";
3292 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3293 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3294 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3295 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3297 const char rtftextA[] = "{\\rtf sometext}";
3298 const char urtftextA[] = "{\\urtf sometext}";
3299 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3300 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3301 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3303 char buf[1024] = {0};
3304 WCHAR bufW[1024] = {0};
3305 LRESULT result;
3307 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3308 any solitary \r to be converted to \r\n on return. Properly paired
3309 \r\n are not affected. It also shows that the special sequence \r\r\n
3310 gets converted to a single space.
3313 #define TEST_SETTEXT(a, b) \
3314 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3315 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3316 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3317 ok (result == lstrlen(buf), \
3318 "WM_GETTEXT returned %ld instead of expected %u\n", \
3319 result, lstrlen(buf)); \
3320 result = strcmp(b, buf); \
3321 ok(result == 0, \
3322 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3324 TEST_SETTEXT(TestItem1, TestItem1)
3325 TEST_SETTEXT(TestItem2, TestItem2_after)
3326 TEST_SETTEXT(TestItem3, TestItem3_after)
3327 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3328 TEST_SETTEXT(TestItem4, TestItem4_after)
3329 TEST_SETTEXT(TestItem5, TestItem5_after)
3330 TEST_SETTEXT(TestItem6, TestItem6_after)
3331 TEST_SETTEXT(TestItem7, TestItem7_after)
3333 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3334 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3335 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3336 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3337 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3338 DestroyWindow(hwndRichEdit);
3339 #undef TEST_SETTEXT
3341 #define TEST_SETTEXTW(a, b) \
3342 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3343 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3344 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3345 ok (result == lstrlenW(bufW), \
3346 "WM_GETTEXT returned %ld instead of expected %u\n", \
3347 result, lstrlenW(bufW)); \
3348 result = lstrcmpW(b, bufW); \
3349 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3351 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3352 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3353 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3354 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3355 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3356 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3357 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3358 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3359 DestroyWindow(hwndRichEdit);
3360 #undef TEST_SETTEXTW
3363 static void test_EM_STREAMOUT(void)
3365 HWND hwndRichEdit = new_richedit(NULL);
3366 int r;
3367 EDITSTREAM es;
3368 char buf[1024] = {0};
3369 char * p;
3371 const char * TestItem1 = "TestSomeText";
3372 const char * TestItem2 = "TestSomeText\r";
3373 const char * TestItem3 = "TestSomeText\r\n";
3375 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3376 p = buf;
3377 es.dwCookie = (DWORD_PTR)&p;
3378 es.dwError = 0;
3379 es.pfnCallback = test_WM_SETTEXT_esCallback;
3380 memset(buf, 0, sizeof(buf));
3381 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3382 r = strlen(buf);
3383 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3384 ok(strcmp(buf, TestItem1) == 0,
3385 "streamed text different, got %s\n", buf);
3387 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3388 p = buf;
3389 es.dwCookie = (DWORD_PTR)&p;
3390 es.dwError = 0;
3391 es.pfnCallback = test_WM_SETTEXT_esCallback;
3392 memset(buf, 0, sizeof(buf));
3393 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3394 r = strlen(buf);
3395 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3396 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3397 ok(strcmp(buf, TestItem3) == 0,
3398 "streamed text different from, got %s\n", buf);
3399 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3400 p = buf;
3401 es.dwCookie = (DWORD_PTR)&p;
3402 es.dwError = 0;
3403 es.pfnCallback = test_WM_SETTEXT_esCallback;
3404 memset(buf, 0, sizeof(buf));
3405 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3406 r = strlen(buf);
3407 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3408 ok(strcmp(buf, TestItem3) == 0,
3409 "streamed text different, got %s\n", buf);
3411 DestroyWindow(hwndRichEdit);
3414 static void test_EM_STREAMOUT_FONTTBL(void)
3416 HWND hwndRichEdit = new_richedit(NULL);
3417 EDITSTREAM es;
3418 char buf[1024] = {0};
3419 char * p;
3420 char * fontTbl;
3421 int brackCount;
3423 const char * TestItem = "TestSomeText";
3425 /* fills in the richedit control with some text */
3426 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3428 /* streams out the text in rtf format */
3429 p = buf;
3430 es.dwCookie = (DWORD_PTR)&p;
3431 es.dwError = 0;
3432 es.pfnCallback = test_WM_SETTEXT_esCallback;
3433 memset(buf, 0, sizeof(buf));
3434 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3436 /* scans for \fonttbl, error if not found */
3437 fontTbl = strstr(buf, "\\fonttbl");
3438 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3439 if(fontTbl)
3441 /* scans for terminating closing bracket */
3442 brackCount = 1;
3443 while(*fontTbl && brackCount)
3445 if(*fontTbl == '{')
3446 brackCount++;
3447 else if(*fontTbl == '}')
3448 brackCount--;
3449 fontTbl++;
3451 /* checks whether closing bracket is ok */
3452 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3453 if(!brackCount)
3455 /* char before closing fonttbl block should be a closed bracket */
3456 fontTbl -= 2;
3457 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3459 /* char after fonttbl block should be a crlf */
3460 fontTbl += 2;
3461 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3464 DestroyWindow(hwndRichEdit);
3468 static void test_EM_SETTEXTEX(void)
3470 HWND hwndRichEdit, parent;
3471 SCROLLINFO si;
3472 int sel_start, sel_end;
3473 SETTEXTEX setText;
3474 GETTEXTEX getText;
3475 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3476 'S', 'o', 'm', 'e',
3477 'T', 'e', 'x', 't', 0};
3478 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3479 't', 'S', 'o', 'm',
3480 'e', 'T', 'e', 'x',
3481 't', 't', 'S', 'o',
3482 'm', 'e', 'T', 'e',
3483 'x', 't', 0};
3484 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3485 '\r','t','S','o','m','e','T','e','x','t',0};
3486 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3487 'S', 'o', 'm', 'e',
3488 'T', 'e', 'x', 't',
3489 '\r', 0};
3490 const char * TestItem2_after = "TestSomeText\r\n";
3491 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3492 'S', 'o', 'm', 'e',
3493 'T', 'e', 'x', 't',
3494 '\r','\n','\r','\n', 0};
3495 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3496 'S', 'o', 'm', 'e',
3497 'T', 'e', 'x', 't',
3498 '\n','\n', 0};
3499 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3500 'S', 'o', 'm', 'e',
3501 'T', 'e', 'x', 't',
3502 '\r','\r', 0};
3503 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3504 'S', 'o', 'm', 'e',
3505 'T', 'e', 'x', 't',
3506 '\r','\r','\n','\r',
3507 '\n', 0};
3508 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3509 'S', 'o', 'm', 'e',
3510 'T', 'e', 'x', 't',
3511 ' ','\r', 0};
3512 #define MAX_BUF_LEN 1024
3513 WCHAR buf[MAX_BUF_LEN];
3514 char bufACP[MAX_BUF_LEN];
3515 char * p;
3516 int result;
3517 CHARRANGE cr;
3518 EDITSTREAM es;
3519 WNDCLASSA cls;
3521 /* Test the scroll position with and without a parent window.
3523 * For some reason the scroll position is 0 after EM_SETTEXTEX
3524 * with the ST_SELECTION flag only when the control has a parent
3525 * window, even though the selection is at the end. */
3526 cls.style = 0;
3527 cls.lpfnWndProc = DefWindowProcA;
3528 cls.cbClsExtra = 0;
3529 cls.cbWndExtra = 0;
3530 cls.hInstance = GetModuleHandleA(0);
3531 cls.hIcon = 0;
3532 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3533 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3534 cls.lpszMenuName = NULL;
3535 cls.lpszClassName = "ParentTestClass";
3536 if(!RegisterClassA(&cls)) assert(0);
3538 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3539 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3540 ok (parent != 0, "Failed to create parent window\n");
3542 hwndRichEdit = CreateWindowEx(0,
3543 RICHEDIT_CLASS, NULL,
3544 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3545 0, 0, 200, 60, parent, NULL,
3546 hmoduleRichEdit, NULL);
3548 setText.codepage = CP_ACP;
3549 setText.flags = ST_SELECTION;
3550 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3551 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3552 si.cbSize = sizeof(si);
3553 si.fMask = SIF_ALL;
3554 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3555 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3556 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3557 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3558 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3560 DestroyWindow(parent);
3562 /* Test without a parent window */
3563 hwndRichEdit = new_richedit(NULL);
3564 setText.codepage = CP_ACP;
3565 setText.flags = ST_SELECTION;
3566 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3567 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3568 si.cbSize = sizeof(si);
3569 si.fMask = SIF_ALL;
3570 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3571 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3572 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3573 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3574 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3576 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3577 * but this time it is because the selection is at the beginning. */
3578 setText.codepage = CP_ACP;
3579 setText.flags = ST_DEFAULT;
3580 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3581 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3582 si.cbSize = sizeof(si);
3583 si.fMask = SIF_ALL;
3584 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3585 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3586 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3587 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3588 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3590 setText.codepage = 1200; /* no constant for unicode */
3591 getText.codepage = 1200; /* no constant for unicode */
3592 getText.cb = MAX_BUF_LEN;
3593 getText.flags = GT_DEFAULT;
3594 getText.lpDefaultChar = NULL;
3595 getText.lpUsedDefChar = NULL;
3597 setText.flags = 0;
3598 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3599 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3600 ok(lstrcmpW(buf, TestItem1) == 0,
3601 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3603 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3604 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3606 setText.codepage = 1200; /* no constant for unicode */
3607 getText.codepage = 1200; /* no constant for unicode */
3608 getText.cb = MAX_BUF_LEN;
3609 getText.flags = GT_DEFAULT;
3610 getText.lpDefaultChar = NULL;
3611 getText.lpUsedDefChar = NULL;
3612 setText.flags = 0;
3613 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3614 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3615 ok(lstrcmpW(buf, TestItem2) == 0,
3616 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3618 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3619 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3620 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3621 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3623 /* Baseline test for just-enough buffer space for string */
3624 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3625 getText.codepage = 1200; /* no constant for unicode */
3626 getText.flags = GT_DEFAULT;
3627 getText.lpDefaultChar = NULL;
3628 getText.lpUsedDefChar = NULL;
3629 memset(buf, 0, MAX_BUF_LEN);
3630 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3631 ok(lstrcmpW(buf, TestItem2) == 0,
3632 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3634 /* When there is enough space for one character, but not both, of the CRLF
3635 pair at the end of the string, the CR is not copied at all. That is,
3636 the caller must not see CRLF pairs truncated to CR at the end of the
3637 string.
3639 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3640 getText.codepage = 1200; /* no constant for unicode */
3641 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3642 getText.lpDefaultChar = NULL;
3643 getText.lpUsedDefChar = NULL;
3644 memset(buf, 0, MAX_BUF_LEN);
3645 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3646 ok(lstrcmpW(buf, TestItem1) == 0,
3647 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3650 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3651 setText.codepage = 1200; /* no constant for unicode */
3652 getText.codepage = 1200; /* no constant for unicode */
3653 getText.cb = MAX_BUF_LEN;
3654 getText.flags = GT_DEFAULT;
3655 getText.lpDefaultChar = NULL;
3656 getText.lpUsedDefChar = NULL;
3657 setText.flags = 0;
3658 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3659 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3660 ok(lstrcmpW(buf, TestItem3_after) == 0,
3661 "EM_SETTEXTEX did not convert properly\n");
3663 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3664 setText.codepage = 1200; /* no constant for unicode */
3665 getText.codepage = 1200; /* no constant for unicode */
3666 getText.cb = MAX_BUF_LEN;
3667 getText.flags = GT_DEFAULT;
3668 getText.lpDefaultChar = NULL;
3669 getText.lpUsedDefChar = NULL;
3670 setText.flags = 0;
3671 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3672 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3673 ok(lstrcmpW(buf, TestItem3_after) == 0,
3674 "EM_SETTEXTEX did not convert properly\n");
3676 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3677 setText.codepage = 1200; /* no constant for unicode */
3678 getText.codepage = 1200; /* no constant for unicode */
3679 getText.cb = MAX_BUF_LEN;
3680 getText.flags = GT_DEFAULT;
3681 getText.lpDefaultChar = NULL;
3682 getText.lpUsedDefChar = NULL;
3683 setText.flags = 0;
3684 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3685 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3686 ok(lstrcmpW(buf, TestItem4_after) == 0,
3687 "EM_SETTEXTEX did not convert properly\n");
3689 /* !ST_SELECTION && Unicode && !\rtf */
3690 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3691 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3693 ok (result == 1,
3694 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3695 ok(lstrlenW(buf) == 0,
3696 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3698 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3699 setText.flags = 0;
3700 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3701 /* select some text */
3702 cr.cpMax = 1;
3703 cr.cpMin = 3;
3704 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3705 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3706 setText.flags = ST_SELECTION;
3707 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3708 ok(result == 0,
3709 "EM_SETTEXTEX with NULL lParam to replace selection"
3710 " with no text should return 0. Got %i\n",
3711 result);
3713 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3714 setText.flags = 0;
3715 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3716 /* select some text */
3717 cr.cpMax = 1;
3718 cr.cpMin = 3;
3719 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3720 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3721 setText.flags = ST_SELECTION;
3722 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3723 (WPARAM)&setText, (LPARAM) TestItem1);
3724 /* get text */
3725 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3726 ok(result == lstrlenW(TestItem1),
3727 "EM_SETTEXTEX with NULL lParam to replace selection"
3728 " with no text should return 0. Got %i\n",
3729 result);
3730 ok(lstrlenW(buf) == 22,
3731 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3732 lstrlenW(buf) );
3734 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3735 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3736 p = (char *)buf;
3737 es.dwCookie = (DWORD_PTR)&p;
3738 es.dwError = 0;
3739 es.pfnCallback = test_WM_SETTEXT_esCallback;
3740 memset(buf, 0, sizeof(buf));
3741 SendMessage(hwndRichEdit, EM_STREAMOUT,
3742 (WPARAM)(SF_RTF), (LPARAM)&es);
3743 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3745 /* !ST_SELECTION && !Unicode && \rtf */
3746 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3747 getText.codepage = 1200; /* no constant for unicode */
3748 getText.cb = MAX_BUF_LEN;
3749 getText.flags = GT_DEFAULT;
3750 getText.lpDefaultChar = NULL;
3751 getText.lpUsedDefChar = NULL;
3753 setText.flags = 0;
3754 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3755 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3756 ok(lstrcmpW(buf, TestItem1) == 0,
3757 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3759 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3760 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3761 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3762 getText.codepage = CP_ACP;
3763 getText.cb = MAX_BUF_LEN;
3764 getText.flags = GT_DEFAULT;
3765 getText.lpDefaultChar = NULL;
3766 getText.lpUsedDefChar = NULL;
3768 setText.flags = ST_SELECTION;
3769 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3770 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3771 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3772 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3773 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3775 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3776 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3777 p = (char *)buf;
3778 es.dwCookie = (DWORD_PTR)&p;
3779 es.dwError = 0;
3780 es.pfnCallback = test_WM_SETTEXT_esCallback;
3781 memset(buf, 0, sizeof(buf));
3782 SendMessage(hwndRichEdit, EM_STREAMOUT,
3783 (WPARAM)(SF_RTF), (LPARAM)&es);
3784 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3786 /* select some text */
3787 cr.cpMax = 1;
3788 cr.cpMin = 3;
3789 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3791 /* ST_SELECTION && !Unicode && \rtf */
3792 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3793 getText.codepage = 1200; /* no constant for unicode */
3794 getText.cb = MAX_BUF_LEN;
3795 getText.flags = GT_DEFAULT;
3796 getText.lpDefaultChar = NULL;
3797 getText.lpUsedDefChar = NULL;
3799 setText.flags = ST_SELECTION;
3800 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3801 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3802 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3804 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3805 setText.codepage = 1200; /* no constant for unicode */
3806 getText.codepage = CP_ACP;
3807 getText.cb = MAX_BUF_LEN;
3809 setText.flags = 0;
3810 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3811 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3813 /* select some text */
3814 cr.cpMax = 1;
3815 cr.cpMin = 3;
3816 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3818 /* ST_SELECTION && !Unicode && !\rtf */
3819 setText.codepage = CP_ACP;
3820 getText.codepage = 1200; /* no constant for unicode */
3821 getText.cb = MAX_BUF_LEN;
3822 getText.flags = GT_DEFAULT;
3823 getText.lpDefaultChar = NULL;
3824 getText.lpUsedDefChar = NULL;
3826 setText.flags = ST_SELECTION;
3827 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3828 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3829 ok(lstrcmpW(buf, TestItem1alt) == 0,
3830 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3831 " using ST_SELECTION and non-Unicode\n");
3833 /* Test setting text using rich text format */
3834 setText.flags = 0;
3835 setText.codepage = CP_ACP;
3836 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3837 getText.codepage = CP_ACP;
3838 getText.cb = MAX_BUF_LEN;
3839 getText.flags = GT_DEFAULT;
3840 getText.lpDefaultChar = NULL;
3841 getText.lpUsedDefChar = NULL;
3842 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3843 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3845 setText.flags = 0;
3846 setText.codepage = CP_ACP;
3847 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3848 getText.codepage = CP_ACP;
3849 getText.cb = MAX_BUF_LEN;
3850 getText.flags = GT_DEFAULT;
3851 getText.lpDefaultChar = NULL;
3852 getText.lpUsedDefChar = NULL;
3853 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3854 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3856 DestroyWindow(hwndRichEdit);
3859 static void test_EM_LIMITTEXT(void)
3861 int ret;
3863 HWND hwndRichEdit = new_richedit(NULL);
3865 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3866 * about setting the length to -1 for multiline edit controls doesn't happen.
3869 /* Don't check default gettextlimit case. That's done in other tests */
3871 /* Set textlimit to 100 */
3872 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3873 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3874 ok (ret == 100,
3875 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3877 /* Set textlimit to 0 */
3878 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3879 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3880 ok (ret == 65536,
3881 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3883 /* Set textlimit to -1 */
3884 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3885 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3886 ok (ret == -1,
3887 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3889 /* Set textlimit to -2 */
3890 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3891 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3892 ok (ret == -2,
3893 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3895 DestroyWindow (hwndRichEdit);
3899 static void test_EM_EXLIMITTEXT(void)
3901 int i, selBegin, selEnd, len1, len2;
3902 int result;
3903 char text[1024 + 1];
3904 char buffer[1024 + 1];
3905 int textlimit = 0; /* multiple of 100 */
3906 HWND hwndRichEdit = new_richedit(NULL);
3908 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3909 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3911 textlimit = 256000;
3912 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3913 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3914 /* set higher */
3915 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3917 textlimit = 1000;
3918 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3919 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3920 /* set lower */
3921 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3923 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3924 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3925 /* default for WParam = 0 */
3926 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3928 textlimit = sizeof(text)-1;
3929 memset(text, 'W', textlimit);
3930 text[sizeof(text)-1] = 0;
3931 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3932 /* maxed out text */
3933 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3935 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3936 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3937 len1 = selEnd - selBegin;
3939 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3940 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3941 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3942 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3943 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3944 len2 = selEnd - selBegin;
3946 ok(len1 != len2,
3947 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3948 len1,len2,i);
3950 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3951 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3952 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3953 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3954 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3955 len1 = selEnd - selBegin;
3957 ok(len1 != len2,
3958 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3959 len1,len2,i);
3961 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3962 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3963 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3964 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3965 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3966 len2 = selEnd - selBegin;
3968 ok(len1 == len2,
3969 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3970 len1,len2,i);
3972 /* set text up to the limit, select all the text, then add a char */
3973 textlimit = 5;
3974 memset(text, 'W', textlimit);
3975 text[textlimit] = 0;
3976 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3977 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3978 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3979 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3980 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3981 result = strcmp(buffer, "A");
3982 ok(0 == result, "got string = \"%s\"\n", buffer);
3984 /* WM_SETTEXT not limited */
3985 textlimit = 10;
3986 memset(text, 'W', textlimit);
3987 text[textlimit] = 0;
3988 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3989 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3990 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3991 i = strlen(buffer);
3992 ok(10 == i, "expected 10 chars\n");
3993 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3994 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3996 /* try inserting more text at end */
3997 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3998 ok(0 == i, "WM_CHAR wasn't processed\n");
3999 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4000 i = strlen(buffer);
4001 ok(10 == i, "expected 10 chars, got %i\n", i);
4002 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4003 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4005 /* try inserting text at beginning */
4006 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4007 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4008 ok(0 == i, "WM_CHAR wasn't processed\n");
4009 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4010 i = strlen(buffer);
4011 ok(10 == i, "expected 10 chars, got %i\n", i);
4012 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4013 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4015 /* WM_CHAR is limited */
4016 textlimit = 1;
4017 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4018 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4019 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4020 ok(0 == i, "WM_CHAR wasn't processed\n");
4021 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4022 ok(0 == i, "WM_CHAR wasn't processed\n");
4023 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4024 i = strlen(buffer);
4025 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4027 DestroyWindow(hwndRichEdit);
4030 static void test_EM_GETLIMITTEXT(void)
4032 int i;
4033 HWND hwndRichEdit = new_richedit(NULL);
4035 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4036 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4038 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4039 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4040 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4042 DestroyWindow(hwndRichEdit);
4045 static void test_WM_SETFONT(void)
4047 /* There is no invalid input or error conditions for this function.
4048 * NULL wParam and lParam just fall back to their default values
4049 * It should be noted that even if you use a gibberish name for your fonts
4050 * here, it will still work because the name is stored. They will display as
4051 * System, but will report their name to be whatever they were created as */
4053 HWND hwndRichEdit = new_richedit(NULL);
4054 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4055 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4056 FF_DONTCARE, "Marlett");
4057 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4058 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4059 FF_DONTCARE, "MS Sans Serif");
4060 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4061 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4062 FF_DONTCARE, "Courier");
4063 LOGFONTA sentLogFont;
4064 CHARFORMAT2A returnedCF2A;
4066 returnedCF2A.cbSize = sizeof(returnedCF2A);
4068 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4069 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4070 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4072 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4073 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4074 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4075 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4077 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4078 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4079 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4080 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4081 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4082 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4084 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4085 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4086 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4087 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4088 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4089 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4091 /* This last test is special since we send in NULL. We clear the variables
4092 * and just compare to "System" instead of the sent in font name. */
4093 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4094 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4095 returnedCF2A.cbSize = sizeof(returnedCF2A);
4097 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4098 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4099 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4100 ok (!strcmp("System",returnedCF2A.szFaceName),
4101 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4103 DestroyWindow(hwndRichEdit);
4107 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4108 LPBYTE pbBuff,
4109 LONG cb,
4110 LONG *pcb)
4112 const char** str = (const char**)dwCookie;
4113 int size = strlen(*str);
4114 if(size > 3) /* let's make it piecemeal for fun */
4115 size = 3;
4116 *pcb = cb;
4117 if (*pcb > size) {
4118 *pcb = size;
4120 if (*pcb > 0) {
4121 memcpy(pbBuff, *str, *pcb);
4122 *str += *pcb;
4124 return 0;
4127 static void test_EM_GETMODIFY(void)
4129 HWND hwndRichEdit = new_richedit(NULL);
4130 LRESULT result;
4131 SETTEXTEX setText;
4132 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4133 'S', 'o', 'm', 'e',
4134 'T', 'e', 'x', 't', 0};
4135 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4136 'S', 'o', 'm', 'e',
4137 'O', 't', 'h', 'e', 'r',
4138 'T', 'e', 'x', 't', 0};
4139 const char* streamText = "hello world";
4140 CHARFORMAT2 cf2;
4141 PARAFORMAT2 pf2;
4142 EDITSTREAM es;
4144 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4145 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4146 FF_DONTCARE, "Courier");
4148 setText.codepage = 1200; /* no constant for unicode */
4149 setText.flags = ST_KEEPUNDO;
4152 /* modify flag shouldn't be set when richedit is first created */
4153 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4154 ok (result == 0,
4155 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4157 /* setting modify flag should actually set it */
4158 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4159 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4160 ok (result != 0,
4161 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4163 /* clearing modify flag should actually clear it */
4164 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4165 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4166 ok (result == 0,
4167 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4169 /* setting font doesn't change modify flag */
4170 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4171 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4172 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4173 ok (result == 0,
4174 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4176 /* setting text should set modify flag */
4177 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4178 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4179 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4180 ok (result != 0,
4181 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4183 /* undo previous text doesn't reset modify flag */
4184 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4185 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4186 ok (result != 0,
4187 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4189 /* set text with no flag to keep undo stack should not set modify flag */
4190 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4191 setText.flags = 0;
4192 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4193 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4194 ok (result == 0,
4195 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4197 /* WM_SETTEXT doesn't modify */
4198 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4199 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4200 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4201 ok (result == 0,
4202 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4204 /* clear the text */
4205 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4206 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4207 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4208 ok (result == 0,
4209 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4211 /* replace text */
4212 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4213 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4214 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4215 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4216 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4217 ok (result != 0,
4218 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4220 /* copy/paste text 1 */
4221 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4222 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4223 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4224 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4225 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4226 ok (result != 0,
4227 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4229 /* copy/paste text 2 */
4230 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4231 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4232 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4233 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4234 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4235 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4236 ok (result != 0,
4237 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4239 /* press char */
4240 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4241 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4242 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4243 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4244 ok (result != 0,
4245 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4247 /* press del */
4248 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4249 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4250 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4251 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4252 ok (result != 0,
4253 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4255 /* set char format */
4256 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4257 cf2.cbSize = sizeof(CHARFORMAT2);
4258 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4259 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4260 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4261 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4262 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4263 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4264 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4265 ok (result != 0,
4266 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4268 /* set para format */
4269 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4270 pf2.cbSize = sizeof(PARAFORMAT2);
4271 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4272 (LPARAM) &pf2);
4273 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4274 pf2.wAlignment = PFA_RIGHT;
4275 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4276 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4277 ok (result == 0,
4278 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4280 /* EM_STREAM */
4281 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4282 es.dwCookie = (DWORD_PTR)&streamText;
4283 es.dwError = 0;
4284 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4285 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4286 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4287 ok (result != 0,
4288 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4290 DestroyWindow(hwndRichEdit);
4293 struct exsetsel_s {
4294 LONG min;
4295 LONG max;
4296 LRESULT expected_retval;
4297 int expected_getsel_start;
4298 int expected_getsel_end;
4299 int _getsel_todo_wine;
4302 const struct exsetsel_s exsetsel_tests[] = {
4303 /* sanity tests */
4304 {5, 10, 10, 5, 10, 0},
4305 {15, 17, 17, 15, 17, 0},
4306 /* test cpMax > strlen() */
4307 {0, 100, 18, 0, 18, 1},
4308 /* test cpMin == cpMax */
4309 {5, 5, 5, 5, 5, 0},
4310 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4311 {-1, 0, 5, 5, 5, 0},
4312 {-1, 17, 5, 5, 5, 0},
4313 {-1, 18, 5, 5, 5, 0},
4314 /* test cpMin < 0 && cpMax < 0 */
4315 {-1, -1, 17, 17, 17, 0},
4316 {-4, -5, 17, 17, 17, 0},
4317 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4318 {0, -1, 18, 0, 18, 1},
4319 {17, -5, 18, 17, 18, 1},
4320 {18, -3, 17, 17, 17, 0},
4321 /* test if cpMin > cpMax */
4322 {15, 19, 18, 15, 18, 1},
4323 {19, 15, 18, 15, 18, 1}
4326 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4327 CHARRANGE cr;
4328 LRESULT result;
4329 int start, end;
4331 cr.cpMin = setsel->min;
4332 cr.cpMax = setsel->max;
4333 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4335 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4337 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4339 if (setsel->_getsel_todo_wine) {
4340 todo_wine {
4341 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);
4343 } else {
4344 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);
4348 static void test_EM_EXSETSEL(void)
4350 HWND hwndRichEdit = new_richedit(NULL);
4351 int i;
4352 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4354 /* sending some text to the window */
4355 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4356 /* 01234567890123456*/
4357 /* 10 */
4359 for (i = 0; i < num_tests; i++) {
4360 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4363 DestroyWindow(hwndRichEdit);
4366 static void test_EM_REPLACESEL(int redraw)
4368 HWND hwndRichEdit = new_richedit(NULL);
4369 char buffer[1024] = {0};
4370 int r;
4371 GETTEXTEX getText;
4372 CHARRANGE cr;
4374 /* sending some text to the window */
4375 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4376 /* 01234567890123456*/
4377 /* 10 */
4379 /* FIXME add more tests */
4380 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4381 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4382 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4383 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4384 r = strcmp(buffer, "testing");
4385 ok(0 == r, "expected %d, got %d\n", 0, r);
4387 DestroyWindow(hwndRichEdit);
4389 hwndRichEdit = new_richedit(NULL);
4391 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4392 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4394 /* Test behavior with carriage returns and newlines */
4395 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4396 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4397 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4398 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4399 r = strcmp(buffer, "RichEdit1");
4400 ok(0 == r, "expected %d, got %d\n", 0, r);
4401 getText.cb = 1024;
4402 getText.codepage = CP_ACP;
4403 getText.flags = GT_DEFAULT;
4404 getText.lpDefaultChar = NULL;
4405 getText.lpUsedDefChar = NULL;
4406 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4407 ok(strcmp(buffer, "RichEdit1") == 0,
4408 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4410 /* Test number of lines reported after EM_REPLACESEL */
4411 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4412 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4414 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4415 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4416 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4417 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4418 r = strcmp(buffer, "RichEdit1\r\n");
4419 ok(0 == r, "expected %d, got %d\n", 0, r);
4420 getText.cb = 1024;
4421 getText.codepage = CP_ACP;
4422 getText.flags = GT_DEFAULT;
4423 getText.lpDefaultChar = NULL;
4424 getText.lpUsedDefChar = NULL;
4425 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4426 ok(strcmp(buffer, "RichEdit1\r") == 0,
4427 "EM_GETTEXTEX returned incorrect string\n");
4429 /* Test number of lines reported after EM_REPLACESEL */
4430 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4431 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4433 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4434 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4435 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4437 /* Test number of lines reported after EM_REPLACESEL */
4438 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4439 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4441 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4442 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4443 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4444 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4446 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4447 r = strcmp(buffer, "RichEdit1\r\n");
4448 ok(0 == r, "expected %d, got %d\n", 0, r);
4449 getText.cb = 1024;
4450 getText.codepage = CP_ACP;
4451 getText.flags = GT_DEFAULT;
4452 getText.lpDefaultChar = NULL;
4453 getText.lpUsedDefChar = NULL;
4454 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4455 ok(strcmp(buffer, "RichEdit1\r") == 0,
4456 "EM_GETTEXTEX returned incorrect string\n");
4458 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4459 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4460 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4461 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4463 /* The following tests show that richedit should handle the special \r\r\n
4464 sequence by turning it into a single space on insertion. However,
4465 EM_REPLACESEL on WinXP returns the number of characters in the original
4466 string.
4469 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4470 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4471 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4472 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4473 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4474 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4475 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4477 /* Test the actual string */
4478 getText.cb = 1024;
4479 getText.codepage = CP_ACP;
4480 getText.flags = GT_DEFAULT;
4481 getText.lpDefaultChar = NULL;
4482 getText.lpUsedDefChar = NULL;
4483 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4484 ok(strcmp(buffer, "\r\r") == 0,
4485 "EM_GETTEXTEX returned incorrect string\n");
4487 /* Test number of lines reported after EM_REPLACESEL */
4488 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4489 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4491 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4492 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4493 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4494 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4495 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4496 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4497 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4499 /* Test the actual string */
4500 getText.cb = 1024;
4501 getText.codepage = CP_ACP;
4502 getText.flags = GT_DEFAULT;
4503 getText.lpDefaultChar = NULL;
4504 getText.lpUsedDefChar = NULL;
4505 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4506 ok(strcmp(buffer, " ") == 0,
4507 "EM_GETTEXTEX returned incorrect string\n");
4509 /* Test number of lines reported after EM_REPLACESEL */
4510 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4511 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4513 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4514 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4515 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4516 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4517 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4518 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4519 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4521 /* Test the actual string */
4522 getText.cb = 1024;
4523 getText.codepage = CP_ACP;
4524 getText.flags = GT_DEFAULT;
4525 getText.lpDefaultChar = NULL;
4526 getText.lpUsedDefChar = NULL;
4527 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4528 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4529 "EM_GETTEXTEX returned incorrect string\n");
4531 /* Test number of lines reported after EM_REPLACESEL */
4532 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4533 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4535 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4536 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4537 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4538 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4539 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4540 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4541 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4543 /* Test the actual string */
4544 getText.cb = 1024;
4545 getText.codepage = CP_ACP;
4546 getText.flags = GT_DEFAULT;
4547 getText.lpDefaultChar = NULL;
4548 getText.lpUsedDefChar = NULL;
4549 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4550 ok(strcmp(buffer, " \r") == 0,
4551 "EM_GETTEXTEX returned incorrect string\n");
4553 /* Test number of lines reported after EM_REPLACESEL */
4554 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4555 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4557 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4558 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4559 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4560 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4561 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4562 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4563 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4565 /* Test the actual string */
4566 getText.cb = 1024;
4567 getText.codepage = CP_ACP;
4568 getText.flags = GT_DEFAULT;
4569 getText.lpDefaultChar = NULL;
4570 getText.lpUsedDefChar = NULL;
4571 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4572 ok(strcmp(buffer, " \r\r") == 0,
4573 "EM_GETTEXTEX returned incorrect string\n");
4575 /* Test number of lines reported after EM_REPLACESEL */
4576 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4577 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4579 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4580 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4581 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4582 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4583 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4584 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4585 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4587 /* Test the actual string */
4588 getText.cb = 1024;
4589 getText.codepage = CP_ACP;
4590 getText.flags = GT_DEFAULT;
4591 getText.lpDefaultChar = NULL;
4592 getText.lpUsedDefChar = NULL;
4593 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4594 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4595 "EM_GETTEXTEX returned incorrect string\n");
4597 /* Test number of lines reported after EM_REPLACESEL */
4598 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4599 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4601 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4602 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4603 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4604 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4605 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4606 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4607 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4609 /* Test the actual string */
4610 getText.cb = 1024;
4611 getText.codepage = CP_ACP;
4612 getText.flags = GT_DEFAULT;
4613 getText.lpDefaultChar = NULL;
4614 getText.lpUsedDefChar = NULL;
4615 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4616 ok(strcmp(buffer, "\r\r") == 0,
4617 "EM_GETTEXTEX returned incorrect string\n");
4619 /* Test number of lines reported after EM_REPLACESEL */
4620 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4621 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4623 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4624 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4625 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4626 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4627 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4628 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4629 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4631 /* Test the actual string */
4632 getText.cb = 1024;
4633 getText.codepage = CP_ACP;
4634 getText.flags = GT_DEFAULT;
4635 getText.lpDefaultChar = NULL;
4636 getText.lpUsedDefChar = NULL;
4637 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4638 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4639 "EM_GETTEXTEX returned incorrect string\n");
4641 /* Test number of lines reported after EM_REPLACESEL */
4642 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4643 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4645 if (!redraw)
4646 /* This is needed to avoid interferring with keybd_event calls
4647 * on other tests that simulate keyboard events. */
4648 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4650 DestroyWindow(hwndRichEdit);
4653 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4654 * to test the state of the modifiers (Ctrl/Alt/Shift).
4656 * Therefore Ctrl-<key> keystrokes need to be simulated with
4657 * keybd_event or by using SetKeyboardState to set the modifiers
4658 * and SendMessage to simulate the keystrokes.
4660 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
4662 LRESULT result;
4663 hold_key(VK_CONTROL);
4664 result = SendMessage(hwnd, WM_KEYDOWN, key, 1);
4665 release_key(VK_CONTROL);
4666 return result;
4669 static void test_WM_PASTE(void)
4671 int result;
4672 char buffer[1024] = {0};
4673 const char* text1 = "testing paste\r";
4674 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4675 const char* text1_after = "testing paste\r\n";
4676 const char* text2 = "testing paste\r\rtesting paste";
4677 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4678 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4679 HWND hwndRichEdit = new_richedit(NULL);
4681 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4682 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4684 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4685 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4686 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4687 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4688 /* Pasted text should be visible at this step */
4689 result = strcmp(text1_step1, buffer);
4690 ok(result == 0,
4691 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4693 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4694 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4695 /* Text should be the same as before (except for \r -> \r\n conversion) */
4696 result = strcmp(text1_after, buffer);
4697 ok(result == 0,
4698 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4700 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4701 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4702 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4703 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4704 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4705 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4706 /* Pasted text should be visible at this step */
4707 result = strcmp(text3, buffer);
4708 ok(result == 0,
4709 "test paste: strcmp = %i\n", result);
4710 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4711 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4712 /* Text should be the same as before (except for \r -> \r\n conversion) */
4713 result = strcmp(text2_after, buffer);
4714 ok(result == 0,
4715 "test paste: strcmp = %i\n", result);
4716 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
4717 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4718 /* Text should revert to post-paste state */
4719 result = strcmp(buffer,text3);
4720 ok(result == 0,
4721 "test paste: strcmp = %i\n", result);
4723 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4724 /* Send WM_CHAR to simulates Ctrl-V */
4725 SendMessage(hwndRichEdit, WM_CHAR, 22,
4726 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4727 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4728 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4729 result = strcmp(buffer,"");
4730 ok(result == 0,
4731 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4733 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4734 * with SetKeyboard state. */
4736 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4737 /* Simulates paste (Ctrl-V) */
4738 hold_key(VK_CONTROL);
4739 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4740 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4741 release_key(VK_CONTROL);
4742 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4743 result = strcmp(buffer,"paste");
4744 ok(result == 0,
4745 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4747 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4748 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4749 /* Simulates copy (Ctrl-C) */
4750 hold_key(VK_CONTROL);
4751 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4752 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) | 1);
4753 release_key(VK_CONTROL);
4754 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4755 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4756 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4757 result = strcmp(buffer,"testing");
4758 ok(result == 0,
4759 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4761 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4762 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4763 /* Simulates select all (Ctrl-A) */
4764 hold_key(VK_CONTROL);
4765 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4766 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) | 1);
4767 /* Simulates select cut (Ctrl-X) */
4768 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4769 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) | 1);
4770 release_key(VK_CONTROL);
4771 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4772 result = strcmp(buffer,"");
4773 ok(result == 0,
4774 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4775 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4776 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4777 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4778 result = strcmp(buffer,"cut\r\n");
4779 todo_wine ok(result == 0,
4780 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4781 /* Simulates undo (Ctrl-Z) */
4782 hold_key(VK_CONTROL);
4783 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4784 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4785 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4786 result = strcmp(buffer,"");
4787 ok(result == 0,
4788 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4789 /* Simulates redo (Ctrl-Y) */
4790 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4791 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4792 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4793 result = strcmp(buffer,"cut\r\n");
4794 todo_wine ok(result == 0,
4795 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4796 release_key(VK_CONTROL);
4798 DestroyWindow(hwndRichEdit);
4801 static void test_EM_FORMATRANGE(void)
4803 int r, i, tpp_x, tpp_y;
4804 HDC hdc;
4805 HWND hwndRichEdit = new_richedit(NULL);
4806 FORMATRANGE fr;
4807 BOOL skip_non_english;
4808 static const struct {
4809 const char *string; /* The string */
4810 int first; /* First 'pagebreak', 0 for don't care */
4811 int second; /* Second 'pagebreak', 0 for don't care */
4812 } fmtstrings[] = {
4813 {"WINE wine", 0, 0},
4814 {"WINE wineWine", 0, 0},
4815 {"WINE\r\nwine\r\nwine", 5, 10},
4816 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4817 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4820 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4821 if (skip_non_english)
4822 skip("Skipping some tests on non-English platform\n");
4824 hdc = GetDC(hwndRichEdit);
4825 ok(hdc != NULL, "Could not get HDC\n");
4827 /* Calculate the twips per pixel */
4828 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4829 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4831 /* Test the simple case where all the text fits in the page rect. */
4832 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4833 fr.hdc = fr.hdcTarget = hdc;
4834 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4835 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4836 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4837 fr.chrg.cpMin = 0;
4838 fr.chrg.cpMax = -1;
4839 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4840 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4842 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4843 fr.rc.bottom = fr.rcPage.bottom;
4844 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4845 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4847 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4849 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4851 GETTEXTLENGTHEX gtl;
4852 SIZE stringsize;
4853 int len;
4855 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4857 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4858 gtl.codepage = CP_ACP;
4859 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4861 /* Get some size information for the string */
4862 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4864 /* Define the box to be half the width needed and a bit larger than the height.
4865 * Changes to the width means we have at least 2 pages. Changes to the height
4866 * is done so we can check the changing of fr.rc.bottom.
4868 fr.hdc = fr.hdcTarget = hdc;
4869 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4870 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4871 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4873 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4874 todo_wine {
4875 ok(r == len, "Expected %d, got %d\n", len, r);
4878 /* We know that the page can't hold the full string. See how many characters
4879 * are on the first one
4881 fr.chrg.cpMin = 0;
4882 fr.chrg.cpMax = -1;
4883 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4884 todo_wine {
4885 if (! skip_non_english)
4886 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4888 if (fmtstrings[i].first)
4889 todo_wine {
4890 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4892 else
4893 ok(r < len, "Expected < %d, got %d\n", len, r);
4895 /* Do another page */
4896 fr.chrg.cpMin = r;
4897 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4898 if (fmtstrings[i].second)
4899 todo_wine {
4900 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4902 else if (! skip_non_english)
4903 ok (r < len, "Expected < %d, got %d\n", len, r);
4905 /* There is at least on more page, but we don't care */
4907 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4908 todo_wine {
4909 ok(r == len, "Expected %d, got %d\n", len, r);
4913 ReleaseDC(NULL, hdc);
4914 DestroyWindow(hwndRichEdit);
4917 static int nCallbackCount = 0;
4919 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4920 LONG cb, LONG* pcb)
4922 const char text[] = {'t','e','s','t'};
4924 if (sizeof(text) <= cb)
4926 if ((int)dwCookie != nCallbackCount)
4928 *pcb = 0;
4929 return 0;
4932 memcpy (pbBuff, text, sizeof(text));
4933 *pcb = sizeof(text);
4935 nCallbackCount++;
4937 return 0;
4939 else
4940 return 1; /* indicates callback failed */
4943 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4944 LPBYTE pbBuff,
4945 LONG cb,
4946 LONG *pcb)
4948 const char** str = (const char**)dwCookie;
4949 int size = strlen(*str);
4950 *pcb = cb;
4951 if (*pcb > size) {
4952 *pcb = size;
4954 if (*pcb > 0) {
4955 memcpy(pbBuff, *str, *pcb);
4956 *str += *pcb;
4958 return 0;
4961 struct StringWithLength {
4962 int length;
4963 char *buffer;
4966 /* This callback is used to handled the null characters in a string. */
4967 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4968 LPBYTE pbBuff,
4969 LONG cb,
4970 LONG *pcb)
4972 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4973 int size = str->length;
4974 *pcb = cb;
4975 if (*pcb > size) {
4976 *pcb = size;
4978 if (*pcb > 0) {
4979 memcpy(pbBuff, str->buffer, *pcb);
4980 str->buffer += *pcb;
4981 str->length -= *pcb;
4983 return 0;
4986 static void test_EM_STREAMIN(void)
4988 HWND hwndRichEdit = new_richedit(NULL);
4989 LRESULT result;
4990 EDITSTREAM es;
4991 char buffer[1024] = {0};
4993 const char * streamText0 = "{\\rtf1 TestSomeText}";
4994 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4995 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4997 const char * streamText1 =
4998 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4999 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5000 "}\r\n";
5002 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5003 const char * streamText2 =
5004 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5005 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5006 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5007 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5008 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5009 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5010 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5012 const char * streamText3 = "RichEdit1";
5014 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5016 const char * streamText4 =
5017 "This text just needs to be long enough to cause run to be split onto "
5018 "two separate lines and make sure the null terminating character is "
5019 "handled properly.\0";
5020 int length4 = strlen(streamText4) + 1;
5021 struct StringWithLength cookieForStream4 = {
5022 length4,
5023 (char *)streamText4,
5026 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5027 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5028 struct StringWithLength cookieForStream5 = {
5029 sizeof(streamText5),
5030 (char *)streamText5,
5033 /* Minimal test without \par at the end */
5034 es.dwCookie = (DWORD_PTR)&streamText0;
5035 es.dwError = 0;
5036 es.pfnCallback = test_EM_STREAMIN_esCallback;
5037 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5038 ok(result == 12, "got %ld, expected %d\n", result, 12);
5040 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5041 ok (result == 12,
5042 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5043 result = strcmp (buffer,"TestSomeText");
5044 ok (result == 0,
5045 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5046 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5048 /* Native richedit 2.0 ignores last \par */
5049 es.dwCookie = (DWORD_PTR)&streamText0a;
5050 es.dwError = 0;
5051 es.pfnCallback = test_EM_STREAMIN_esCallback;
5052 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5053 ok(result == 12, "got %ld, expected %d\n", result, 12);
5055 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5056 ok (result == 12,
5057 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5058 result = strcmp (buffer,"TestSomeText");
5059 ok (result == 0,
5060 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5061 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5063 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5064 es.dwCookie = (DWORD_PTR)&streamText0b;
5065 es.dwError = 0;
5066 es.pfnCallback = test_EM_STREAMIN_esCallback;
5067 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5068 ok(result == 13, "got %ld, expected %d\n", result, 13);
5070 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5071 ok (result == 14,
5072 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5073 result = strcmp (buffer,"TestSomeText\r\n");
5074 ok (result == 0,
5075 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5076 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5078 es.dwCookie = (DWORD_PTR)&streamText1;
5079 es.dwError = 0;
5080 es.pfnCallback = test_EM_STREAMIN_esCallback;
5081 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5082 ok(result == 12, "got %ld, expected %d\n", result, 12);
5084 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5085 ok (result == 12,
5086 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5087 result = strcmp (buffer,"TestSomeText");
5088 ok (result == 0,
5089 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5090 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5092 es.dwCookie = (DWORD_PTR)&streamText2;
5093 es.dwError = 0;
5094 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5095 ok(result == 0, "got %ld, expected %d\n", result, 0);
5097 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5098 ok (result == 0,
5099 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5100 ok (strlen(buffer) == 0,
5101 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5102 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5104 es.dwCookie = (DWORD_PTR)&streamText3;
5105 es.dwError = 0;
5106 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5107 ok(result == 0, "got %ld, expected %d\n", result, 0);
5109 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5110 ok (result == 0,
5111 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5112 ok (strlen(buffer) == 0,
5113 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5114 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5116 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5117 es.dwError = 0;
5118 es.pfnCallback = test_EM_STREAMIN_esCallback;
5119 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5120 ok(result == 18, "got %ld, expected %d\n", result, 18);
5122 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5123 todo_wine ok(result == 15,
5124 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5125 result = strcmp (buffer,"TestUTF8WithBOM");
5126 todo_wine ok(result == 0,
5127 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5128 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5130 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5131 es.dwError = 0;
5132 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5133 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5134 ok(result == length4, "got %ld, expected %d\n", result, length4);
5136 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5137 ok (result == length4,
5138 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5139 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5141 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5142 es.dwError = 0;
5143 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5144 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5145 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5147 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5148 ok (result == length5,
5149 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5150 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5152 DestroyWindow(hwndRichEdit);
5155 static void test_EM_StreamIn_Undo(void)
5157 /* The purpose of this test is to determine when a EM_StreamIn should be
5158 * undoable. This is important because WM_PASTE currently uses StreamIn and
5159 * pasting should always be undoable but streaming isn't always.
5161 * cases to test:
5162 * StreamIn plain text without SFF_SELECTION.
5163 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5164 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5165 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5166 * Feel free to add tests for other text modes or StreamIn things.
5170 HWND hwndRichEdit = new_richedit(NULL);
5171 LRESULT result;
5172 EDITSTREAM es;
5173 char buffer[1024] = {0};
5174 const char randomtext[] = "Some text";
5176 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5178 /* StreamIn, no SFF_SELECTION */
5179 es.dwCookie = nCallbackCount;
5180 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5181 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5182 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5183 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5184 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5185 result = strcmp (buffer,"test");
5186 ok (result == 0,
5187 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5189 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5190 ok (result == FALSE,
5191 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5193 /* StreamIn, SFF_SELECTION, but nothing selected */
5194 es.dwCookie = nCallbackCount;
5195 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5196 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5197 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5198 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5199 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5200 result = strcmp (buffer,"testSome text");
5201 ok (result == 0,
5202 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5204 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5205 ok (result == TRUE,
5206 "EM_STREAMIN with SFF_SELECTION but no selection set "
5207 "should create an undo\n");
5209 /* StreamIn, SFF_SELECTION, with a selection */
5210 es.dwCookie = nCallbackCount;
5211 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5212 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5213 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5214 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5215 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5216 result = strcmp (buffer,"Sometesttext");
5217 ok (result == 0,
5218 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5220 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5221 ok (result == TRUE,
5222 "EM_STREAMIN with SFF_SELECTION and selection set "
5223 "should create an undo\n");
5225 DestroyWindow(hwndRichEdit);
5228 static BOOL is_em_settextex_supported(HWND hwnd)
5230 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5231 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5234 static void test_unicode_conversions(void)
5236 static const WCHAR tW[] = {'t',0};
5237 static const WCHAR teW[] = {'t','e',0};
5238 static const WCHAR textW[] = {'t','e','s','t',0};
5239 static const char textA[] = "test";
5240 char bufA[64];
5241 WCHAR bufW[64];
5242 HWND hwnd;
5243 int em_settextex_supported, ret;
5245 #define set_textA(hwnd, wm_set_text, txt) \
5246 do { \
5247 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5248 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5249 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5250 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5251 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5252 } while(0)
5253 #define expect_textA(hwnd, wm_get_text, txt) \
5254 do { \
5255 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5256 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5257 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5258 memset(bufA, 0xAA, sizeof(bufA)); \
5259 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5260 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5261 ret = lstrcmpA(bufA, txt); \
5262 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5263 } while(0)
5265 #define set_textW(hwnd, wm_set_text, txt) \
5266 do { \
5267 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5268 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5269 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5270 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5271 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5272 } while(0)
5273 #define expect_textW(hwnd, wm_get_text, txt) \
5274 do { \
5275 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5276 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5277 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5278 memset(bufW, 0xAA, sizeof(bufW)); \
5279 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5280 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5281 ret = lstrcmpW(bufW, txt); \
5282 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5283 } while(0)
5284 #define expect_empty(hwnd, wm_get_text) \
5285 do { \
5286 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5287 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5288 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5289 memset(bufA, 0xAA, sizeof(bufA)); \
5290 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5291 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5292 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5293 } while(0)
5295 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5296 0, 0, 200, 60, 0, 0, 0, 0);
5297 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5299 ret = IsWindowUnicode(hwnd);
5300 ok(ret, "RichEdit20W should be unicode under NT\n");
5302 /* EM_SETTEXTEX is supported starting from version 3.0 */
5303 em_settextex_supported = is_em_settextex_supported(hwnd);
5304 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5305 em_settextex_supported ? "" : "NOT ");
5307 expect_empty(hwnd, WM_GETTEXT);
5308 expect_empty(hwnd, EM_GETTEXTEX);
5310 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5311 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5312 expect_textA(hwnd, WM_GETTEXT, "t");
5313 expect_textA(hwnd, EM_GETTEXTEX, "t");
5314 expect_textW(hwnd, EM_GETTEXTEX, tW);
5316 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5317 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5318 expect_textA(hwnd, WM_GETTEXT, "te");
5319 expect_textA(hwnd, EM_GETTEXTEX, "te");
5320 expect_textW(hwnd, EM_GETTEXTEX, teW);
5322 set_textA(hwnd, WM_SETTEXT, NULL);
5323 expect_empty(hwnd, WM_GETTEXT);
5324 expect_empty(hwnd, EM_GETTEXTEX);
5326 set_textA(hwnd, WM_SETTEXT, textA);
5327 expect_textA(hwnd, WM_GETTEXT, textA);
5328 expect_textA(hwnd, EM_GETTEXTEX, textA);
5329 expect_textW(hwnd, EM_GETTEXTEX, textW);
5331 if (em_settextex_supported)
5333 set_textA(hwnd, EM_SETTEXTEX, textA);
5334 expect_textA(hwnd, WM_GETTEXT, textA);
5335 expect_textA(hwnd, EM_GETTEXTEX, textA);
5336 expect_textW(hwnd, EM_GETTEXTEX, textW);
5339 set_textW(hwnd, WM_SETTEXT, textW);
5340 expect_textW(hwnd, WM_GETTEXT, textW);
5341 expect_textA(hwnd, WM_GETTEXT, textA);
5342 expect_textW(hwnd, EM_GETTEXTEX, textW);
5343 expect_textA(hwnd, EM_GETTEXTEX, textA);
5345 if (em_settextex_supported)
5347 set_textW(hwnd, EM_SETTEXTEX, textW);
5348 expect_textW(hwnd, WM_GETTEXT, textW);
5349 expect_textA(hwnd, WM_GETTEXT, textA);
5350 expect_textW(hwnd, EM_GETTEXTEX, textW);
5351 expect_textA(hwnd, EM_GETTEXTEX, textA);
5353 DestroyWindow(hwnd);
5355 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5356 0, 0, 200, 60, 0, 0, 0, 0);
5357 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5359 ret = IsWindowUnicode(hwnd);
5360 ok(!ret, "RichEdit20A should NOT be unicode\n");
5362 set_textA(hwnd, WM_SETTEXT, textA);
5363 expect_textA(hwnd, WM_GETTEXT, textA);
5364 expect_textA(hwnd, EM_GETTEXTEX, textA);
5365 expect_textW(hwnd, EM_GETTEXTEX, textW);
5367 if (em_settextex_supported)
5369 set_textA(hwnd, EM_SETTEXTEX, textA);
5370 expect_textA(hwnd, WM_GETTEXT, textA);
5371 expect_textA(hwnd, EM_GETTEXTEX, textA);
5372 expect_textW(hwnd, EM_GETTEXTEX, textW);
5375 set_textW(hwnd, WM_SETTEXT, textW);
5376 expect_textW(hwnd, WM_GETTEXT, textW);
5377 expect_textA(hwnd, WM_GETTEXT, textA);
5378 expect_textW(hwnd, EM_GETTEXTEX, textW);
5379 expect_textA(hwnd, EM_GETTEXTEX, textA);
5381 if (em_settextex_supported)
5383 set_textW(hwnd, EM_SETTEXTEX, textW);
5384 expect_textW(hwnd, WM_GETTEXT, textW);
5385 expect_textA(hwnd, WM_GETTEXT, textA);
5386 expect_textW(hwnd, EM_GETTEXTEX, textW);
5387 expect_textA(hwnd, EM_GETTEXTEX, textA);
5389 DestroyWindow(hwnd);
5392 static void test_WM_CHAR(void)
5394 HWND hwnd;
5395 int ret;
5396 const char * char_list = "abc\rabc\r";
5397 const char * expected_content_single = "abcabc";
5398 const char * expected_content_multi = "abc\r\nabc\r\n";
5399 char buffer[64] = {0};
5400 const char * p;
5402 /* single-line control must IGNORE carriage returns */
5403 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5404 0, 0, 200, 60, 0, 0, 0, 0);
5405 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5407 p = char_list;
5408 while (*p != '\0') {
5409 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5410 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5411 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5412 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5413 p++;
5416 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5417 ret = strcmp(buffer, expected_content_single);
5418 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5420 DestroyWindow(hwnd);
5422 /* multi-line control inserts CR normally */
5423 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5424 0, 0, 200, 60, 0, 0, 0, 0);
5425 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5427 p = char_list;
5428 while (*p != '\0') {
5429 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5430 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5431 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5432 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5433 p++;
5436 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5437 ret = strcmp(buffer, expected_content_multi);
5438 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5440 DestroyWindow(hwnd);
5443 static void test_EM_GETTEXTLENGTHEX(void)
5445 HWND hwnd;
5446 GETTEXTLENGTHEX gtl;
5447 int ret;
5448 const char * base_string = "base string";
5449 const char * test_string = "a\nb\n\n\r\n";
5450 const char * test_string_after = "a";
5451 const char * test_string_2 = "a\rtest\rstring";
5452 char buffer[64] = {0};
5454 /* single line */
5455 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5456 0, 0, 200, 60, 0, 0, 0, 0);
5457 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5459 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5460 gtl.codepage = CP_ACP;
5461 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5462 ok(ret == 0, "ret %d\n",ret);
5464 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5465 gtl.codepage = CP_ACP;
5466 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5467 ok(ret == 0, "ret %d\n",ret);
5469 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5471 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5472 gtl.codepage = CP_ACP;
5473 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5474 ok(ret == strlen(base_string), "ret %d\n",ret);
5476 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5477 gtl.codepage = CP_ACP;
5478 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5479 ok(ret == strlen(base_string), "ret %d\n",ret);
5481 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5483 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5484 gtl.codepage = CP_ACP;
5485 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5486 ok(ret == 1, "ret %d\n",ret);
5488 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5489 gtl.codepage = CP_ACP;
5490 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5491 ok(ret == 1, "ret %d\n",ret);
5493 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5494 ret = strcmp(buffer, test_string_after);
5495 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5497 DestroyWindow(hwnd);
5499 /* multi line */
5500 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5501 0, 0, 200, 60, 0, 0, 0, 0);
5502 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5504 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5505 gtl.codepage = CP_ACP;
5506 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5507 ok(ret == 0, "ret %d\n",ret);
5509 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5510 gtl.codepage = CP_ACP;
5511 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5512 ok(ret == 0, "ret %d\n",ret);
5514 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5516 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5517 gtl.codepage = CP_ACP;
5518 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5519 ok(ret == strlen(base_string), "ret %d\n",ret);
5521 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5522 gtl.codepage = CP_ACP;
5523 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5524 ok(ret == strlen(base_string), "ret %d\n",ret);
5526 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5528 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5529 gtl.codepage = CP_ACP;
5530 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5531 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5533 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5534 gtl.codepage = CP_ACP;
5535 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5536 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5538 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5540 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5541 gtl.codepage = CP_ACP;
5542 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5543 ok(ret == 10, "ret %d\n",ret);
5545 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5546 gtl.codepage = CP_ACP;
5547 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5548 ok(ret == 6, "ret %d\n",ret);
5550 /* Unicode/NUMCHARS/NUMBYTES */
5551 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5553 gtl.flags = GTL_DEFAULT;
5554 gtl.codepage = 1200;
5555 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5556 ok(ret == lstrlen(test_string_2),
5557 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlen(test_string_2));
5559 gtl.flags = GTL_NUMCHARS;
5560 gtl.codepage = 1200;
5561 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5562 ok(ret == lstrlen(test_string_2),
5563 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlen(test_string_2));
5565 gtl.flags = GTL_NUMBYTES;
5566 gtl.codepage = 1200;
5567 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5568 ok(ret == lstrlen(test_string_2)*2,
5569 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5571 gtl.flags = GTL_PRECISE;
5572 gtl.codepage = 1200;
5573 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5574 ok(ret == lstrlen(test_string_2)*2,
5575 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5577 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5578 gtl.codepage = 1200;
5579 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5580 ok(ret == lstrlen(test_string_2),
5581 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2));
5583 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5584 gtl.codepage = 1200;
5585 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0);
5586 ok(ret == E_INVALIDARG,
5587 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5589 DestroyWindow(hwnd);
5593 /* globals that parent and child access when checking event masks & notifications */
5594 static HWND eventMaskEditHwnd = 0;
5595 static int queriedEventMask;
5596 static int watchForEventMask = 0;
5598 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5599 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5601 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5603 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5605 return DefWindowProcA(hwnd, message, wParam, lParam);
5608 /* test event masks in combination with WM_COMMAND */
5609 static void test_eventMask(void)
5611 HWND parent;
5612 int ret, style;
5613 WNDCLASSA cls;
5614 const char text[] = "foo bar\n";
5615 int eventMask;
5617 /* register class to capture WM_COMMAND */
5618 cls.style = 0;
5619 cls.lpfnWndProc = ParentMsgCheckProcA;
5620 cls.cbClsExtra = 0;
5621 cls.cbWndExtra = 0;
5622 cls.hInstance = GetModuleHandleA(0);
5623 cls.hIcon = 0;
5624 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5625 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5626 cls.lpszMenuName = NULL;
5627 cls.lpszClassName = "EventMaskParentClass";
5628 if(!RegisterClassA(&cls)) assert(0);
5630 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5631 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5632 ok (parent != 0, "Failed to create parent window\n");
5634 eventMaskEditHwnd = new_richedit(parent);
5635 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5637 eventMask = ENM_CHANGE | ENM_UPDATE;
5638 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5639 ok(ret == ENM_NONE, "wrong event mask\n");
5640 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5641 ok(ret == eventMask, "failed to set event mask\n");
5643 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5644 queriedEventMask = 0; /* initialize to something other than we expect */
5645 watchForEventMask = EN_CHANGE;
5646 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5647 ok(ret == TRUE, "failed to set text\n");
5648 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5649 notification in response to WM_SETTEXT */
5650 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5651 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5653 /* check to see if EN_CHANGE is sent when redraw is turned off */
5654 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5655 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5656 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5657 /* redraw is disabled by making the window invisible. */
5658 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5659 queriedEventMask = 0; /* initialize to something other than we expect */
5660 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5661 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5662 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5663 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5664 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5666 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5667 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5668 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5669 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5670 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5671 watchForEventMask = EN_UPDATE;
5672 queriedEventMask = 0; /* initialize to something other than we expect */
5673 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5674 ok(queriedEventMask == 0,
5675 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5676 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5677 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5678 queriedEventMask = 0; /* initialize to something other than we expect */
5679 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5680 ok(queriedEventMask == eventMask,
5681 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5684 DestroyWindow(parent);
5687 static int received_WM_NOTIFY = 0;
5688 static int modify_at_WM_NOTIFY = 0;
5689 static BOOL filter_on_WM_NOTIFY = FALSE;
5690 static HWND hwndRichedit_WM_NOTIFY;
5692 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5694 if(message == WM_NOTIFY)
5696 received_WM_NOTIFY = 1;
5697 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5698 if (filter_on_WM_NOTIFY) return TRUE;
5700 return DefWindowProcA(hwnd, message, wParam, lParam);
5703 static void test_WM_NOTIFY(void)
5705 HWND parent;
5706 WNDCLASSA cls;
5707 CHARFORMAT2 cf2;
5708 int sel_start, sel_end;
5710 /* register class to capture WM_NOTIFY */
5711 cls.style = 0;
5712 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5713 cls.cbClsExtra = 0;
5714 cls.cbWndExtra = 0;
5715 cls.hInstance = GetModuleHandleA(0);
5716 cls.hIcon = 0;
5717 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5718 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5719 cls.lpszMenuName = NULL;
5720 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5721 if(!RegisterClassA(&cls)) assert(0);
5723 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5724 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5725 ok (parent != 0, "Failed to create parent window\n");
5727 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5728 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5730 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5732 /* Notifications for selection change should only be sent when selection
5733 actually changes. EM_SETCHARFORMAT is one message that calls
5734 ME_CommitUndo, which should check whether message should be sent */
5735 received_WM_NOTIFY = 0;
5736 cf2.cbSize = sizeof(CHARFORMAT2);
5737 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5738 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5739 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5740 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5741 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5743 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5744 already at 0. */
5745 received_WM_NOTIFY = 0;
5746 modify_at_WM_NOTIFY = 0;
5747 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5748 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5749 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5751 received_WM_NOTIFY = 0;
5752 modify_at_WM_NOTIFY = 0;
5753 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5754 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5756 received_WM_NOTIFY = 0;
5757 modify_at_WM_NOTIFY = 0;
5758 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5759 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5760 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5762 /* Test for WM_NOTIFY messages with redraw disabled. */
5763 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5764 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5765 received_WM_NOTIFY = 0;
5766 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5767 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5768 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5770 /* Test filtering key events. */
5771 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5772 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5773 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5774 received_WM_NOTIFY = 0;
5775 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5776 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5777 ok(sel_start == 1 && sel_end == 1,
5778 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5779 filter_on_WM_NOTIFY = TRUE;
5780 received_WM_NOTIFY = 0;
5781 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5782 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5783 ok(sel_start == 1 && sel_end == 1,
5784 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5786 /* test with owner set to NULL */
5787 SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5788 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5789 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5790 ok(sel_start == 1 && sel_end == 1,
5791 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5793 DestroyWindow(hwndRichedit_WM_NOTIFY);
5794 DestroyWindow(parent);
5797 static void test_undo_coalescing(void)
5799 HWND hwnd;
5800 int result;
5801 char buffer[64] = {0};
5803 /* multi-line control inserts CR normally */
5804 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5805 0, 0, 200, 60, 0, 0, 0, 0);
5806 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5808 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5809 ok (result == FALSE, "Can undo after window creation.\n");
5810 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5811 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5812 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5813 ok (result == FALSE, "Can redo after window creation.\n");
5814 result = SendMessage(hwnd, EM_REDO, 0, 0);
5815 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5817 /* Test the effect of arrows keys during typing on undo transactions*/
5818 simulate_typing_characters(hwnd, "one two three");
5819 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5820 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5821 simulate_typing_characters(hwnd, " four five six");
5823 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5824 ok (result == FALSE, "Can redo before anything is undone.\n");
5825 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5826 ok (result == TRUE, "Cannot undo typed characters.\n");
5827 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5828 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5829 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5830 ok (result == TRUE, "Cannot redo after undo.\n");
5831 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5832 result = strcmp(buffer, "one two three");
5833 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5835 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5836 ok (result == TRUE, "Cannot undo typed characters.\n");
5837 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5838 ok (result == TRUE, "Failed to undo typed characters.\n");
5839 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5840 result = strcmp(buffer, "");
5841 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5843 /* Test the effect of focus changes during typing on undo transactions*/
5844 simulate_typing_characters(hwnd, "one two three");
5845 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5846 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5847 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5848 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5849 simulate_typing_characters(hwnd, " four five six");
5850 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5851 ok (result == TRUE, "Failed to undo typed characters.\n");
5852 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5853 result = strcmp(buffer, "one two three");
5854 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5856 /* Test the effect of the back key during typing on undo transactions */
5857 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5858 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5859 ok (result == TRUE, "Failed to clear the text.\n");
5860 simulate_typing_characters(hwnd, "one two threa");
5861 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5862 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5863 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5864 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5865 simulate_typing_characters(hwnd, "e four five six");
5866 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5867 ok (result == TRUE, "Failed to undo typed characters.\n");
5868 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5869 result = strcmp(buffer, "");
5870 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5872 /* Test the effect of the delete key during typing on undo transactions */
5873 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5874 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5875 ok(result == TRUE, "Failed to set the text.\n");
5876 SendMessage(hwnd, EM_SETSEL, 1, 1);
5877 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5878 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5879 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5880 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5881 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5882 ok (result == TRUE, "Failed to undo typed characters.\n");
5883 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5884 result = strcmp(buffer, "acd");
5885 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5886 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5887 ok (result == TRUE, "Failed to undo typed characters.\n");
5888 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5889 result = strcmp(buffer, "abcd");
5890 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5892 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5893 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5894 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5895 ok (result == TRUE, "Failed to clear the text.\n");
5896 simulate_typing_characters(hwnd, "one two three");
5897 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5898 ok (result == 0, "expected %d but got %d\n", 0, result);
5899 simulate_typing_characters(hwnd, " four five six");
5900 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5901 ok (result == TRUE, "Failed to undo typed characters.\n");
5902 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5903 result = strcmp(buffer, "one two three");
5904 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5905 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5906 ok (result == TRUE, "Failed to undo typed characters.\n");
5907 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5908 result = strcmp(buffer, "");
5909 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5911 DestroyWindow(hwnd);
5914 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5916 int length;
5918 /* MSDN lied, length is actually the number of bytes. */
5919 length = bytes / sizeof(WCHAR);
5920 switch(code)
5922 case WB_ISDELIMITER:
5923 return text[pos] == 'X';
5924 case WB_LEFT:
5925 case WB_MOVEWORDLEFT:
5926 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5927 return pos-1;
5928 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5929 case WB_LEFTBREAK:
5930 pos--;
5931 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5932 pos--;
5933 return pos;
5934 case WB_RIGHT:
5935 case WB_MOVEWORDRIGHT:
5936 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5937 return pos+1;
5938 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5939 case WB_RIGHTBREAK:
5940 pos++;
5941 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5942 pos++;
5943 return pos;
5944 default:
5945 ok(FALSE, "Unexpected code %d\n", code);
5946 break;
5948 return 0;
5951 static void test_word_movement(void)
5953 HWND hwnd;
5954 int result;
5955 int sel_start, sel_end;
5956 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5958 /* multi-line control inserts CR normally */
5959 hwnd = new_richedit(NULL);
5961 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5962 ok (result == TRUE, "Failed to clear the text.\n");
5963 SendMessage(hwnd, EM_SETSEL, 0, 0);
5964 /* |one two three */
5966 send_ctrl_key(hwnd, VK_RIGHT);
5967 /* one |two three */
5968 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5969 ok(sel_start == sel_end, "Selection should be empty\n");
5970 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5972 send_ctrl_key(hwnd, VK_RIGHT);
5973 /* one two |three */
5974 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5975 ok(sel_start == sel_end, "Selection should be empty\n");
5976 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5978 send_ctrl_key(hwnd, VK_LEFT);
5979 /* one |two three */
5980 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5981 ok(sel_start == sel_end, "Selection should be empty\n");
5982 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5984 send_ctrl_key(hwnd, VK_LEFT);
5985 /* |one two three */
5986 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5987 ok(sel_start == sel_end, "Selection should be empty\n");
5988 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5990 SendMessage(hwnd, EM_SETSEL, 8, 8);
5991 /* one two | three */
5992 send_ctrl_key(hwnd, VK_RIGHT);
5993 /* one two |three */
5994 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5995 ok(sel_start == sel_end, "Selection should be empty\n");
5996 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5998 SendMessage(hwnd, EM_SETSEL, 11, 11);
5999 /* one two th|ree */
6000 send_ctrl_key(hwnd, VK_LEFT);
6001 /* one two |three */
6002 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6003 ok(sel_start == sel_end, "Selection should be empty\n");
6004 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6006 /* Test with a custom word break procedure that uses X as the delimiter. */
6007 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6008 ok (result == TRUE, "Failed to clear the text.\n");
6009 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6010 /* |one twoXthree */
6011 send_ctrl_key(hwnd, VK_RIGHT);
6012 /* one twoX|three */
6013 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6014 ok(sel_start == sel_end, "Selection should be empty\n");
6015 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6017 DestroyWindow(hwnd);
6019 /* Make sure the behaviour is the same with a unicode richedit window,
6020 * and using unicode functions. */
6022 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6023 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6024 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6026 /* Test with a custom word break procedure that uses X as the delimiter. */
6027 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6028 ok (result == TRUE, "Failed to clear the text.\n");
6029 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6030 /* |one twoXthree */
6031 send_ctrl_key(hwnd, VK_RIGHT);
6032 /* one twoX|three */
6033 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6034 ok(sel_start == sel_end, "Selection should be empty\n");
6035 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6037 DestroyWindow(hwnd);
6040 static void test_EM_CHARFROMPOS(void)
6042 HWND hwnd;
6043 int result;
6044 RECT rcClient;
6045 POINTL point;
6046 point.x = 0;
6047 point.y = 40;
6049 /* multi-line control inserts CR normally */
6050 hwnd = new_richedit(NULL);
6051 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6052 (LPARAM)"one two three four five six seven\reight");
6053 ok(result == 1, "Expected 1, got %d\n", result);
6054 GetClientRect(hwnd, &rcClient);
6056 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6057 ok(result == 34, "expected character index of 34 but got %d\n", result);
6059 /* Test with points outside the bounds of the richedit control. */
6060 point.x = -1;
6061 point.y = 40;
6062 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6063 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6065 point.x = 1000;
6066 point.y = 0;
6067 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6068 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6070 point.x = 1000;
6071 point.y = 36;
6072 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6073 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6075 point.x = 1000;
6076 point.y = -1;
6077 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6078 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6080 point.x = 1000;
6081 point.y = rcClient.bottom + 1;
6082 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6083 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6085 point.x = 1000;
6086 point.y = rcClient.bottom;
6087 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6088 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6090 DestroyWindow(hwnd);
6093 static void test_word_wrap(void)
6095 HWND hwnd;
6096 POINTL point = {0, 60}; /* This point must be below the first line */
6097 const char *text = "Must be long enough to test line wrapping";
6098 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6099 int res, pos, lines;
6101 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6102 * when specified on window creation and set later. */
6103 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6104 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6105 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6106 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6107 ok(res, "WM_SETTEXT failed.\n");
6108 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6109 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6110 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6111 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6113 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6114 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6115 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6116 DestroyWindow(hwnd);
6118 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6119 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6120 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6122 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6123 ok(res, "WM_SETTEXT failed.\n");
6124 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6125 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6126 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6127 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6129 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6130 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6131 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6132 DestroyWindow(hwnd);
6134 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6135 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6136 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6137 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6138 ok(res, "WM_SETTEXT failed.\n");
6139 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6140 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6142 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6143 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6144 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6145 DestroyWindow(hwnd);
6147 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6148 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6149 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6150 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6151 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6152 ok(res, "WM_SETTEXT failed.\n");
6153 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6154 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6156 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6157 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6158 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6160 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6161 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6162 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6163 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6164 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6166 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6167 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6168 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6169 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6170 DestroyWindow(hwnd);
6172 /* Test to see if wrapping happens with redraw disabled. */
6173 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6174 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6175 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6176 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6177 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6178 ok(res, "EM_REPLACESEL failed.\n");
6179 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6180 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6181 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6182 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6183 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6185 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6186 DestroyWindow(hwnd);
6189 static void test_autoscroll(void)
6191 HWND hwnd = new_richedit(NULL);
6192 int lines, ret, redraw;
6193 POINT pt;
6195 for (redraw = 0; redraw <= 1; redraw++) {
6196 trace("testing with WM_SETREDRAW=%d\n", redraw);
6197 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6198 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6199 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6200 ok(lines == 8, "%d lines instead of 8\n", lines);
6201 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6202 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6203 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6204 ret = GetWindowLong(hwnd, GWL_STYLE);
6205 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6207 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6208 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6209 ok(lines == 1, "%d lines instead of 1\n", lines);
6210 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6211 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6212 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6213 ret = GetWindowLong(hwnd, GWL_STYLE);
6214 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6217 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6218 DestroyWindow(hwnd);
6220 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6221 * auto vertical/horizontal scrolling options. */
6222 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6223 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6224 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6225 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6226 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6227 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6228 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6229 ret = GetWindowLong(hwnd, GWL_STYLE);
6230 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6231 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6232 DestroyWindow(hwnd);
6234 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6235 WS_POPUP|ES_MULTILINE,
6236 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6237 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6238 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6239 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6240 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6241 ret = GetWindowLong(hwnd, GWL_STYLE);
6242 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6243 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6244 DestroyWindow(hwnd);
6248 static void test_format_rect(void)
6250 HWND hwnd;
6251 RECT rc, expected, clientRect;
6252 int n;
6253 DWORD options;
6255 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6256 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6257 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6258 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6260 GetClientRect(hwnd, &clientRect);
6262 expected = clientRect;
6263 expected.left += 1;
6264 expected.right -= 1;
6265 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6266 ok(rc.top == expected.top && rc.left == expected.left &&
6267 rc.bottom == expected.bottom && rc.right == expected.right,
6268 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6269 rc.top, rc.left, rc.bottom, rc.right,
6270 expected.top, expected.left, expected.bottom, expected.right);
6272 for (n = -3; n <= 3; n++)
6274 rc = clientRect;
6275 rc.top += n;
6276 rc.left += n;
6277 rc.bottom -= n;
6278 rc.right -= n;
6279 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6281 expected = rc;
6282 expected.top = max(0, rc.top);
6283 expected.left = max(0, rc.left);
6284 expected.bottom = min(clientRect.bottom, rc.bottom);
6285 expected.right = min(clientRect.right, rc.right);
6286 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6287 ok(rc.top == expected.top && rc.left == expected.left &&
6288 rc.bottom == expected.bottom && rc.right == expected.right,
6289 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6290 n, rc.top, rc.left, rc.bottom, rc.right,
6291 expected.top, expected.left, expected.bottom, expected.right);
6294 rc = clientRect;
6295 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6296 expected = clientRect;
6297 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6298 ok(rc.top == expected.top && rc.left == expected.left &&
6299 rc.bottom == expected.bottom && rc.right == expected.right,
6300 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6301 rc.top, rc.left, rc.bottom, rc.right,
6302 expected.top, expected.left, expected.bottom, expected.right);
6304 /* Adding the selectionbar adds the selectionbar width to the left side. */
6305 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6306 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6307 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6308 expected.left += 8; /* selection bar width */
6309 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6310 ok(rc.top == expected.top && rc.left == expected.left &&
6311 rc.bottom == expected.bottom && rc.right == expected.right,
6312 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6313 rc.top, rc.left, rc.bottom, rc.right,
6314 expected.top, expected.left, expected.bottom, expected.right);
6316 rc = clientRect;
6317 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6318 expected = clientRect;
6319 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6320 ok(rc.top == expected.top && rc.left == expected.left &&
6321 rc.bottom == expected.bottom && rc.right == expected.right,
6322 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6323 rc.top, rc.left, rc.bottom, rc.right,
6324 expected.top, expected.left, expected.bottom, expected.right);
6326 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6327 * even if the left side is already 0. */
6328 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6329 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6330 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6331 expected.left -= 8; /* selection bar width */
6332 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6333 ok(rc.top == expected.top && rc.left == expected.left &&
6334 rc.bottom == expected.bottom && rc.right == expected.right,
6335 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6336 rc.top, rc.left, rc.bottom, rc.right,
6337 expected.top, expected.left, expected.bottom, expected.right);
6339 /* Set the absolute value of the formatting rectangle. */
6340 rc = clientRect;
6341 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6342 expected = clientRect;
6343 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6344 ok(rc.top == expected.top && rc.left == expected.left &&
6345 rc.bottom == expected.bottom && rc.right == expected.right,
6346 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6347 n, rc.top, rc.left, rc.bottom, rc.right,
6348 expected.top, expected.left, expected.bottom, expected.right);
6350 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6351 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6352 * tests show that this isn't true. */
6353 rc.top = 15;
6354 rc.left = 15;
6355 rc.bottom = clientRect.bottom - 15;
6356 rc.right = clientRect.right - 15;
6357 expected = rc;
6358 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6359 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6360 ok(rc.top == expected.top && rc.left == expected.left &&
6361 rc.bottom == expected.bottom && rc.right == expected.right,
6362 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6363 rc.top, rc.left, rc.bottom, rc.right,
6364 expected.top, expected.left, expected.bottom, expected.right);
6366 /* For some reason it does not limit the values to the client rect with
6367 * a WPARAM value of 1. */
6368 rc.top = -15;
6369 rc.left = -15;
6370 rc.bottom = clientRect.bottom + 15;
6371 rc.right = clientRect.right + 15;
6372 expected = rc;
6373 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6374 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6375 ok(rc.top == expected.top && rc.left == expected.left &&
6376 rc.bottom == expected.bottom && rc.right == expected.right,
6377 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6378 rc.top, rc.left, rc.bottom, rc.right,
6379 expected.top, expected.left, expected.bottom, expected.right);
6381 /* Reset to default rect and check how the format rect adjusts to window
6382 * resize and how it copes with very small windows */
6383 SendMessageA(hwnd, EM_SETRECT, 0, 0);
6385 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6386 GetClientRect(hwnd, &clientRect);
6388 expected = clientRect;
6389 expected.left += 1;
6390 expected.right -= 1;
6391 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6392 ok(rc.top == expected.top && rc.left == expected.left &&
6393 rc.bottom == expected.bottom && rc.right == expected.right,
6394 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6395 rc.top, rc.left, rc.bottom, rc.right,
6396 expected.top, expected.left, expected.bottom, expected.right);
6398 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6399 GetClientRect(hwnd, &clientRect);
6401 expected = clientRect;
6402 expected.left += 1;
6403 expected.right -= 1;
6404 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6405 ok(rc.top == expected.top && rc.left == expected.left &&
6406 rc.bottom == expected.bottom && rc.right == expected.right,
6407 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6408 rc.top, rc.left, rc.bottom, rc.right,
6409 expected.top, expected.left, expected.bottom, expected.right);
6411 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6412 GetClientRect(hwnd, &clientRect);
6414 expected = clientRect;
6415 expected.left += 1;
6416 expected.right -= 1;
6417 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6418 ok(rc.top == expected.top && rc.left == expected.left &&
6419 rc.bottom == expected.bottom && rc.right == expected.right,
6420 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6421 rc.top, rc.left, rc.bottom, rc.right,
6422 expected.top, expected.left, expected.bottom, expected.right);
6424 DestroyWindow(hwnd);
6426 /* The extended window style affects the formatting rectangle. */
6427 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6428 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6429 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6430 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6432 GetClientRect(hwnd, &clientRect);
6434 expected = clientRect;
6435 expected.left += 1;
6436 expected.top += 1;
6437 expected.right -= 1;
6438 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6439 ok(rc.top == expected.top && rc.left == expected.left &&
6440 rc.bottom == expected.bottom && rc.right == expected.right,
6441 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6442 rc.top, rc.left, rc.bottom, rc.right,
6443 expected.top, expected.left, expected.bottom, expected.right);
6445 rc = clientRect;
6446 rc.top += 5;
6447 rc.left += 5;
6448 rc.bottom -= 5;
6449 rc.right -= 5;
6450 expected = rc;
6451 expected.top -= 1;
6452 expected.left -= 1;
6453 expected.right += 1;
6454 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6455 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6456 ok(rc.top == expected.top && rc.left == expected.left &&
6457 rc.bottom == expected.bottom && rc.right == expected.right,
6458 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6459 rc.top, rc.left, rc.bottom, rc.right,
6460 expected.top, expected.left, expected.bottom, expected.right);
6462 DestroyWindow(hwnd);
6465 static void test_WM_GETDLGCODE(void)
6467 HWND hwnd;
6468 UINT res, expected;
6469 MSG msg;
6471 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6473 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6474 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6475 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6476 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6477 msg.hwnd = hwnd;
6478 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6479 expected = expected | DLGC_WANTMESSAGE;
6480 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6481 res, expected);
6482 DestroyWindow(hwnd);
6484 msg.message = WM_KEYDOWN;
6485 msg.wParam = VK_RETURN;
6486 msg.lParam = (MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6487 msg.pt.x = 0;
6488 msg.pt.y = 0;
6489 msg.time = GetTickCount();
6491 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6492 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6493 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6494 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6495 msg.hwnd = hwnd;
6496 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6497 expected = expected | DLGC_WANTMESSAGE;
6498 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6499 res, expected);
6500 DestroyWindow(hwnd);
6502 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6503 ES_MULTILINE|WS_POPUP,
6504 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6505 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6506 msg.hwnd = hwnd;
6507 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6508 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6509 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6510 res, expected);
6511 DestroyWindow(hwnd);
6513 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6514 ES_WANTRETURN|WS_POPUP,
6515 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6516 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6517 msg.hwnd = hwnd;
6518 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6519 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6520 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6521 res, expected);
6522 DestroyWindow(hwnd);
6524 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6525 WS_POPUP,
6526 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6527 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6528 msg.hwnd = hwnd;
6529 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6530 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6531 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6532 res, expected);
6533 DestroyWindow(hwnd);
6535 msg.wParam = VK_TAB;
6536 msg.lParam = (MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6538 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6539 ES_MULTILINE|WS_POPUP,
6540 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6541 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6542 msg.hwnd = hwnd;
6543 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6544 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6545 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6546 res, expected);
6547 DestroyWindow(hwnd);
6549 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6550 WS_POPUP,
6551 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6552 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6553 msg.hwnd = hwnd;
6554 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6555 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6556 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6557 res, expected);
6558 DestroyWindow(hwnd);
6560 hold_key(VK_CONTROL);
6562 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6563 ES_MULTILINE|WS_POPUP,
6564 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6565 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6566 msg.hwnd = hwnd;
6567 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6568 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6569 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6570 res, expected);
6571 DestroyWindow(hwnd);
6573 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6574 WS_POPUP,
6575 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6576 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6577 msg.hwnd = hwnd;
6578 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6579 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6580 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6581 res, expected);
6582 DestroyWindow(hwnd);
6584 release_key(VK_CONTROL);
6586 msg.wParam = 'a';
6587 msg.lParam = (MapVirtualKey('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6589 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6590 ES_MULTILINE|WS_POPUP,
6591 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6592 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6593 msg.hwnd = hwnd;
6594 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6595 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6596 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6597 res, expected);
6598 DestroyWindow(hwnd);
6600 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6601 WS_POPUP,
6602 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6603 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6604 msg.hwnd = hwnd;
6605 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6606 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6607 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6608 res, expected);
6609 DestroyWindow(hwnd);
6611 msg.message = WM_CHAR;
6613 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6614 ES_MULTILINE|WS_POPUP,
6615 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6616 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6617 msg.hwnd = hwnd;
6618 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6619 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6620 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6621 res, expected);
6622 DestroyWindow(hwnd);
6624 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6625 WS_POPUP,
6626 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6627 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6628 msg.hwnd = hwnd;
6629 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6630 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6631 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6632 res, expected);
6633 DestroyWindow(hwnd);
6636 static void test_zoom(void)
6638 HWND hwnd;
6639 UINT ret;
6640 RECT rc;
6641 POINT pt;
6642 int numerator, denominator;
6644 hwnd = new_richedit(NULL);
6645 GetClientRect(hwnd, &rc);
6646 pt.x = (rc.right - rc.left) / 2;
6647 pt.y = (rc.bottom - rc.top) / 2;
6648 ClientToScreen(hwnd, &pt);
6650 /* Test initial zoom value */
6651 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6652 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6653 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6654 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6656 /* test scroll wheel */
6657 hold_key(VK_CONTROL);
6658 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6659 MAKELPARAM(pt.x, pt.y));
6660 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6661 release_key(VK_CONTROL);
6663 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6664 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6665 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6666 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6668 /* Test how much the mouse wheel can zoom in and out. */
6669 ret = SendMessage(hwnd, EM_SETZOOM, 490, 100);
6670 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6672 hold_key(VK_CONTROL);
6673 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6674 MAKELPARAM(pt.x, pt.y));
6675 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6676 release_key(VK_CONTROL);
6678 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6679 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6680 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6681 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6683 ret = SendMessage(hwnd, EM_SETZOOM, 491, 100);
6684 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6686 hold_key(VK_CONTROL);
6687 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6688 MAKELPARAM(pt.x, pt.y));
6689 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6690 release_key(VK_CONTROL);
6692 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6693 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6694 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6695 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6697 ret = SendMessage(hwnd, EM_SETZOOM, 20, 100);
6698 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6700 hold_key(VK_CONTROL);
6701 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6702 MAKELPARAM(pt.x, pt.y));
6703 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6704 release_key(VK_CONTROL);
6706 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6707 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6708 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6709 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6711 ret = SendMessage(hwnd, EM_SETZOOM, 19, 100);
6712 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6714 hold_key(VK_CONTROL);
6715 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6716 MAKELPARAM(pt.x, pt.y));
6717 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6718 release_key(VK_CONTROL);
6720 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6721 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6722 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6723 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6725 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6726 ret = SendMessage(hwnd, EM_SETZOOM, 50, 13);
6727 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6729 hold_key(VK_CONTROL);
6730 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6731 MAKELPARAM(pt.x, pt.y));
6732 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6733 release_key(VK_CONTROL);
6735 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6736 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6737 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6738 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6740 /* Test bounds checking on EM_SETZOOM */
6741 ret = SendMessage(hwnd, EM_SETZOOM, 2, 127);
6742 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6744 ret = SendMessage(hwnd, EM_SETZOOM, 127, 2);
6745 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6747 ret = SendMessage(hwnd, EM_SETZOOM, 2, 128);
6748 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6750 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6751 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6752 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6753 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6755 ret = SendMessage(hwnd, EM_SETZOOM, 128, 2);
6756 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6758 /* See if negative numbers are accepted. */
6759 ret = SendMessage(hwnd, EM_SETZOOM, -100, -100);
6760 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6762 /* See if negative numbers are accepted. */
6763 ret = SendMessage(hwnd, EM_SETZOOM, 0, 100);
6764 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6766 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6767 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6768 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6769 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6771 /* Reset the zoom value */
6772 ret = SendMessage(hwnd, EM_SETZOOM, 0, 0);
6773 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6775 DestroyWindow(hwnd);
6778 struct dialog_mode_messages
6780 int wm_getdefid, wm_close, wm_nextdlgctl;
6783 static struct dialog_mode_messages dm_messages;
6785 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6786 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6787 "got %d\n", wmclose, dm_messages.wm_close); \
6788 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6789 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6790 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6791 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6793 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6795 switch (iMsg)
6797 case DM_GETDEFID:
6798 dm_messages.wm_getdefid++;
6799 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6800 case WM_NEXTDLGCTL:
6801 dm_messages.wm_nextdlgctl++;
6802 break;
6803 case WM_CLOSE:
6804 dm_messages.wm_close++;
6805 break;
6808 return DefWindowProc(hwnd, iMsg, wParam, lParam);
6811 static void test_dialogmode(void)
6813 HWND hwRichEdit, hwParent, hwButton;
6814 MSG msg= {0};
6815 int lcount, r;
6816 WNDCLASSA cls;
6818 cls.style = 0;
6819 cls.lpfnWndProc = dialog_mode_wnd_proc;
6820 cls.cbClsExtra = 0;
6821 cls.cbWndExtra = 0;
6822 cls.hInstance = GetModuleHandleA(0);
6823 cls.hIcon = 0;
6824 cls.hCursor = LoadCursorA(0, IDC_ARROW);
6825 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6826 cls.lpszMenuName = NULL;
6827 cls.lpszClassName = "DialogModeParentClass";
6828 if(!RegisterClassA(&cls)) assert(0);
6830 hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6831 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6833 /* Test richedit(ES_MULTILINE) */
6835 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6837 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6838 ok(0 == r, "expected 0, got %d\n", r);
6839 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6840 ok(2 == lcount, "expected 2, got %d\n", lcount);
6842 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6843 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6845 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6846 ok(0 == r, "expected 0, got %d\n", r);
6847 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6848 ok(3 == lcount, "expected 3, got %d\n", lcount);
6850 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6851 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6852 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6853 ok(0 == r, "expected 0, got %d\n", r);
6854 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6855 ok(3 == lcount, "expected 3, got %d\n", lcount);
6857 DestroyWindow(hwRichEdit);
6859 /* Test standalone richedit(ES_MULTILINE) */
6861 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6863 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6864 ok(0 == r, "expected 0, got %d\n", r);
6865 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6866 ok(2 == lcount, "expected 2, got %d\n", lcount);
6868 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6869 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6871 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6872 ok(0 == r, "expected 0, got %d\n", r);
6873 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6874 ok(2 == lcount, "expected 2, got %d\n", lcount);
6876 DestroyWindow(hwRichEdit);
6878 /* Check a destination for messages */
6880 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6882 SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6883 SetParent( hwRichEdit, NULL);
6885 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6886 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6888 memset(&dm_messages, 0, sizeof(dm_messages));
6889 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6890 ok(0 == r, "expected 0, got %d\n", r);
6891 test_dm_messages(0, 1, 0);
6893 memset(&dm_messages, 0, sizeof(dm_messages));
6894 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6895 ok(0 == r, "expected 0, got %d\n", r);
6896 test_dm_messages(0, 0, 1);
6898 DestroyWindow(hwRichEdit);
6900 /* Check messages from richedit(ES_MULTILINE) */
6902 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6904 memset(&dm_messages, 0, sizeof(dm_messages));
6905 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6906 ok(0 == r, "expected 0, got %d\n", r);
6907 test_dm_messages(0, 0, 0);
6909 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6910 ok(2 == lcount, "expected 2, got %d\n", lcount);
6912 memset(&dm_messages, 0, sizeof(dm_messages));
6913 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6914 ok(0 == r, "expected 0, got %d\n", r);
6915 test_dm_messages(0, 0, 0);
6917 memset(&dm_messages, 0, sizeof(dm_messages));
6918 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6919 ok(0 == r, "expected 0, got %d\n", r);
6920 test_dm_messages(0, 0, 0);
6922 memset(&dm_messages, 0, sizeof(dm_messages));
6923 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6924 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6925 test_dm_messages(0, 0, 0);
6927 memset(&dm_messages, 0, sizeof(dm_messages));
6928 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6929 ok(0 == r, "expected 0, got %d\n", r);
6930 test_dm_messages(0, 1, 0);
6932 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6933 ok(2 == lcount, "expected 2, got %d\n", lcount);
6935 memset(&dm_messages, 0, sizeof(dm_messages));
6936 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6937 ok(0 == r, "expected 0, got %d\n", r);
6938 test_dm_messages(0, 0, 0);
6940 memset(&dm_messages, 0, sizeof(dm_messages));
6941 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6942 ok(0 == r, "expected 0, got %d\n", r);
6943 test_dm_messages(0, 0, 1);
6945 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6946 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6947 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6949 memset(&dm_messages, 0, sizeof(dm_messages));
6950 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6951 ok(0 == r, "expected 0, got %d\n", r);
6952 test_dm_messages(0, 1, 1);
6954 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6955 ok(2 == lcount, "expected 2, got %d\n", lcount);
6957 DestroyWindow(hwButton);
6958 DestroyWindow(hwRichEdit);
6960 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
6962 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, hwParent);
6964 memset(&dm_messages, 0, sizeof(dm_messages));
6965 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6966 ok(0 == r, "expected 0, got %d\n", r);
6967 test_dm_messages(0, 0, 0);
6969 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6970 ok(2 == lcount, "expected 2, got %d\n", lcount);
6972 memset(&dm_messages, 0, sizeof(dm_messages));
6973 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6974 ok(0 == r, "expected 0, got %d\n", r);
6975 test_dm_messages(0, 0, 0);
6977 memset(&dm_messages, 0, sizeof(dm_messages));
6978 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6979 ok(0 == r, "expected 0, got %d\n", r);
6980 test_dm_messages(0, 0, 0);
6982 memset(&dm_messages, 0, sizeof(dm_messages));
6983 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6984 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6985 test_dm_messages(0, 0, 0);
6987 memset(&dm_messages, 0, sizeof(dm_messages));
6988 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6989 ok(0 == r, "expected 0, got %d\n", r);
6990 test_dm_messages(0, 0, 0);
6992 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6993 ok(3 == lcount, "expected 3, got %d\n", lcount);
6995 memset(&dm_messages, 0, sizeof(dm_messages));
6996 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6997 ok(0 == r, "expected 0, got %d\n", r);
6998 test_dm_messages(0, 0, 0);
7000 memset(&dm_messages, 0, sizeof(dm_messages));
7001 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7002 ok(0 == r, "expected 0, got %d\n", r);
7003 test_dm_messages(0, 0, 1);
7005 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7006 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7007 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7009 memset(&dm_messages, 0, sizeof(dm_messages));
7010 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7011 ok(0 == r, "expected 0, got %d\n", r);
7012 test_dm_messages(0, 0, 0);
7014 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7015 ok(4 == lcount, "expected 4, got %d\n", lcount);
7017 DestroyWindow(hwButton);
7018 DestroyWindow(hwRichEdit);
7020 /* Check messages from richedit(0) */
7022 hwRichEdit = new_window(RICHEDIT_CLASS, 0, hwParent);
7024 memset(&dm_messages, 0, sizeof(dm_messages));
7025 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7026 ok(0 == r, "expected 0, got %d\n", r);
7027 test_dm_messages(0, 0, 0);
7029 memset(&dm_messages, 0, sizeof(dm_messages));
7030 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7031 ok(0 == r, "expected 0, got %d\n", r);
7032 test_dm_messages(0, 0, 0);
7034 memset(&dm_messages, 0, sizeof(dm_messages));
7035 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7036 ok(0 == r, "expected 0, got %d\n", r);
7037 test_dm_messages(0, 0, 0);
7039 memset(&dm_messages, 0, sizeof(dm_messages));
7040 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7041 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7042 test_dm_messages(0, 0, 0);
7044 memset(&dm_messages, 0, sizeof(dm_messages));
7045 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7046 ok(0 == r, "expected 0, got %d\n", r);
7047 test_dm_messages(0, 1, 0);
7049 memset(&dm_messages, 0, sizeof(dm_messages));
7050 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7051 ok(0 == r, "expected 0, got %d\n", r);
7052 test_dm_messages(0, 0, 0);
7054 memset(&dm_messages, 0, sizeof(dm_messages));
7055 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7056 ok(0 == r, "expected 0, got %d\n", r);
7057 test_dm_messages(0, 0, 1);
7059 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7060 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7061 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7063 memset(&dm_messages, 0, sizeof(dm_messages));
7064 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7065 ok(0 == r, "expected 0, got %d\n", r);
7066 test_dm_messages(0, 1, 1);
7068 DestroyWindow(hwRichEdit);
7070 /* Check messages from richedit(ES_WANTRETURN) */
7072 hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7074 memset(&dm_messages, 0, sizeof(dm_messages));
7075 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7076 ok(0 == r, "expected 0, got %d\n", r);
7077 test_dm_messages(0, 0, 0);
7079 memset(&dm_messages, 0, sizeof(dm_messages));
7080 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7081 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7082 test_dm_messages(0, 0, 0);
7084 memset(&dm_messages, 0, sizeof(dm_messages));
7085 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7086 ok(0 == r, "expected 0, got %d\n", r);
7087 test_dm_messages(0, 0, 0);
7089 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7090 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7091 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7093 memset(&dm_messages, 0, sizeof(dm_messages));
7094 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7095 ok(0 == r, "expected 0, got %d\n", r);
7096 test_dm_messages(0, 0, 0);
7098 DestroyWindow(hwRichEdit);
7099 DestroyWindow(hwParent);
7102 static void test_EM_FINDWORDBREAK_W(void)
7104 static const struct {
7105 WCHAR c;
7106 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7107 } delimiter_tests[] = {
7108 {0x0a, FALSE}, /* newline */
7109 {0x0b, FALSE}, /* vertical tab */
7110 {0x0c, FALSE}, /* form feed */
7111 {0x0d, FALSE}, /* carriage return */
7112 {0x20, TRUE}, /* space */
7113 {0x61, FALSE}, /* capital letter a */
7114 {0xa0, FALSE}, /* no-break space */
7115 {0x2000, FALSE}, /* en quad */
7116 {0x3000, FALSE}, /* Ideographic space */
7117 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7118 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7119 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7120 {0xac00, FALSE}, /* Hangul character GA*/
7121 {0xd7af, FALSE}, /* End of Hangul character chart */
7122 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7123 {0xff20, FALSE}, /* fullwidth commercial @ */
7124 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7126 int i;
7127 HWND hwndRichEdit = new_richeditW(NULL);
7128 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7129 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7131 WCHAR wbuf[2];
7132 int result;
7134 wbuf[0] = delimiter_tests[i].c;
7135 wbuf[1] = 0;
7136 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7137 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7138 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7139 todo_wine
7140 ok(result == delimiter_tests[i].isdelimiter,
7141 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7142 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7143 else
7144 ok(result == delimiter_tests[i].isdelimiter,
7145 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7146 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7148 DestroyWindow(hwndRichEdit);
7151 static void test_EM_FINDWORDBREAK_A(void)
7153 static const struct {
7154 WCHAR c;
7155 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7156 } delimiter_tests[] = {
7157 {0x0a, FALSE}, /* newline */
7158 {0x0b, FALSE}, /* vertical tab */
7159 {0x0c, FALSE}, /* form feed */
7160 {0x0d, FALSE}, /* carriage return */
7161 {0x20, TRUE}, /* space */
7162 {0x61, FALSE}, /* capital letter a */
7164 int i;
7165 HWND hwndRichEdit = new_richedit(NULL);
7167 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7168 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7170 int result;
7171 char buf[2];
7172 buf[0] = delimiter_tests[i].c;
7173 buf[1] = 0;
7174 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7175 result = SendMessage(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7176 if (buf[0] == 0x20)
7177 todo_wine
7178 ok(result == delimiter_tests[i].isdelimiter,
7179 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7180 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7181 else
7182 ok(result == delimiter_tests[i].isdelimiter,
7183 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7184 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7186 DestroyWindow(hwndRichEdit);
7190 * This test attempts to show the effect of enter on a richedit
7191 * control v1.0 inserts CRLF whereas for higher versions it only
7192 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7193 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7194 * does for higher. The same test is cloned in riched32 and riched20.
7196 static void test_enter(void)
7198 static const struct {
7199 const char *initialtext;
7200 const int cursor;
7201 const char *expectedwmtext;
7202 const char *expectedemtext;
7203 const char *expectedemtextcrlf;
7204 } testenteritems[] = {
7205 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7206 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7207 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7208 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7209 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7212 char expectedbuf[1024];
7213 char resultbuf[1024];
7214 HWND hwndRichEdit = new_richedit(NULL);
7215 UINT i,j;
7217 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7219 char buf[1024] = {0};
7220 LRESULT result;
7221 GETTEXTEX getText;
7222 const char *expected;
7224 /* Set the text to the initial text */
7225 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) testenteritems[i].initialtext);
7226 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7228 /* Send Enter */
7229 SendMessage(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7230 simulate_typing_characters(hwndRichEdit, "\r");
7232 /* 1. Retrieve with WM_GETTEXT */
7233 buf[0] = 0x00;
7234 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
7235 expected = testenteritems[i].expectedwmtext;
7237 resultbuf[0]=0x00;
7238 for (j = 0; j < (UINT)result; j++)
7239 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7240 expectedbuf[0] = '\0';
7241 for (j = 0; j < strlen(expected); j++)
7242 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7244 result = strcmp(expected, buf);
7245 ok (result == 0,
7246 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7247 i, resultbuf, expectedbuf);
7249 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7250 getText.cb = sizeof(buf);
7251 getText.flags = GT_DEFAULT;
7252 getText.codepage = CP_ACP;
7253 getText.lpDefaultChar = NULL;
7254 getText.lpUsedDefChar = NULL;
7255 buf[0] = 0x00;
7256 result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
7257 expected = testenteritems[i].expectedemtext;
7259 resultbuf[0]=0x00;
7260 for (j = 0; j < (UINT)result; j++)
7261 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7262 expectedbuf[0] = '\0';
7263 for (j = 0; j < strlen(expected); j++)
7264 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7266 result = strcmp(expected, buf);
7267 ok (result == 0,
7268 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7269 i, resultbuf, expectedbuf);
7271 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7272 getText.cb = sizeof(buf);
7273 getText.flags = GT_USECRLF;
7274 getText.codepage = CP_ACP;
7275 getText.lpDefaultChar = NULL;
7276 getText.lpUsedDefChar = NULL;
7277 buf[0] = 0x00;
7278 result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
7279 expected = testenteritems[i].expectedemtextcrlf;
7281 resultbuf[0]=0x00;
7282 for (j = 0; j < (UINT)result; j++)
7283 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7284 expectedbuf[0] = '\0';
7285 for (j = 0; j < strlen(expected); j++)
7286 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7288 result = strcmp(expected, buf);
7289 ok (result == 0,
7290 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7291 i, resultbuf, expectedbuf);
7294 DestroyWindow(hwndRichEdit);
7297 START_TEST( editor )
7299 BOOL ret;
7300 /* Must explicitly LoadLibrary(). The test has no references to functions in
7301 * RICHED20.DLL, so the linker doesn't actually link to it. */
7302 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
7303 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7305 test_WM_CHAR();
7306 test_EM_FINDTEXT();
7307 test_EM_GETLINE();
7308 test_EM_POSFROMCHAR();
7309 test_EM_SCROLLCARET();
7310 test_EM_SCROLL();
7311 test_scrollbar_visibility();
7312 test_WM_SETTEXT();
7313 test_EM_LINELENGTH();
7314 test_EM_SETCHARFORMAT();
7315 test_EM_SETTEXTMODE();
7316 test_TM_PLAINTEXT();
7317 test_EM_SETOPTIONS();
7318 test_WM_GETTEXT();
7319 test_EM_GETTEXTRANGE();
7320 test_EM_GETSELTEXT();
7321 test_EM_SETUNDOLIMIT();
7322 test_ES_PASSWORD();
7323 test_EM_SETTEXTEX();
7324 test_EM_LIMITTEXT();
7325 test_EM_EXLIMITTEXT();
7326 test_EM_GETLIMITTEXT();
7327 test_WM_SETFONT();
7328 test_EM_GETMODIFY();
7329 test_EM_EXSETSEL();
7330 test_WM_PASTE();
7331 test_EM_STREAMIN();
7332 test_EM_STREAMOUT();
7333 test_EM_STREAMOUT_FONTTBL();
7334 test_EM_StreamIn_Undo();
7335 test_EM_FORMATRANGE();
7336 test_unicode_conversions();
7337 test_EM_GETTEXTLENGTHEX();
7338 test_EM_REPLACESEL(1);
7339 test_EM_REPLACESEL(0);
7340 test_WM_NOTIFY();
7341 test_EM_AUTOURLDETECT();
7342 test_eventMask();
7343 test_undo_coalescing();
7344 test_word_movement();
7345 test_EM_CHARFROMPOS();
7346 test_SETPARAFORMAT();
7347 test_word_wrap();
7348 test_autoscroll();
7349 test_format_rect();
7350 test_WM_GETDLGCODE();
7351 test_zoom();
7352 test_dialogmode();
7353 test_EM_FINDWORDBREAK_W();
7354 test_EM_FINDWORDBREAK_A();
7355 test_enter();
7357 /* Set the environment variable WINETEST_RICHED20 to keep windows
7358 * responsive and open for 30 seconds. This is useful for debugging.
7360 if (getenv( "WINETEST_RICHED20" )) {
7361 keep_responsive(30);
7364 OleFlushClipboard();
7365 ret = FreeLibrary(hmoduleRichEdit);
7366 ok(ret, "error: %d\n", (int) GetLastError());